Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
SeasonFacet
Compiler Version
v0.7.6+commit.7338295f
Optimization Enabled:
Yes with 100 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {Weather, SafeMath, C} from "./Weather.sol"; import {LibIncentive} from "contracts/libraries/LibIncentive.sol"; import {LibTransfer} from "contracts/libraries/Token/LibTransfer.sol"; import {LibWell} from "contracts/libraries/Well/LibWell.sol"; import {LibGauge} from "contracts/libraries/LibGauge.sol"; import {LibWhitelistedTokens} from "contracts/libraries/Silo/LibWhitelistedTokens.sol"; import {LibGerminate} from "contracts/libraries/Silo/LibGerminate.sol"; /** * @title SeasonFacet * @author Publius, Chaikitty, Brean * @notice Holds the Sunrise function and handles all logic for Season changes. */ contract SeasonFacet is Weather { using SafeMath for uint256; /** * @notice Emitted when the Season changes. * @param season The new Season number */ event Sunrise(uint256 indexed season); //////////////////// SUNRISE //////////////////// /** * @notice Advances Beanstalk to the next Season, sending reward Beans to the caller's circulating balance. * @return reward The number of beans minted to the caller. */ function sunrise() external payable returns (uint256) { return gm(msg.sender, LibTransfer.To.EXTERNAL); } /** * @notice Advances Beanstalk to the next Season, sending reward Beans to a specified address & balance. * @param account Indicates to which address reward Beans should be sent * @param mode Indicates whether the reward beans are sent to internal or circulating balance * @return reward The number of Beans minted to the caller. */ function gm(address account, LibTransfer.To mode) public payable returns (uint256) { uint256 initialGasLeft = gasleft(); require(!s.paused, "Season: Paused."); require(seasonTime() > s.season.current, "Season: Still current Season."); uint32 season = stepSeason(); int256 deltaB = stepOracle(); LibGerminate.endTotalGermination(season, LibWhitelistedTokens.getWhitelistedTokens()); uint256 caseId = calcCaseIdandUpdate(deltaB); LibGauge.stepGauge(); stepSun(deltaB, caseId); return incentivize(account, initialGasLeft, mode); } /** * @notice Returns the expected Season number given the current block timestamp. * {sunrise} can be called when `seasonTime() > s.season.current`. */ function seasonTime() public view virtual returns (uint32) { if (block.timestamp < s.season.start) return 0; if (s.season.period == 0) return type(uint32).max; return uint32((block.timestamp - s.season.start) / s.season.period); // Note: SafeMath is redundant here. } //////////////////// SEASON INTERNAL //////////////////// /** * @dev Moves the Season forward by 1. */ function stepSeason() private returns (uint32 season) { s.season.current += 1; season = s.season.current; s.season.sunriseBlock = uint32(block.number); // Note: Will overflow in the year 3650. emit Sunrise(season); } /** * @param account The address to which the reward beans are sent, may or may not * be the same as the caller of `sunrise()` * @param initialGasLeft The amount of gas left at the start of the transaction * @param mode Send reward beans to Internal or Circulating balance * @dev Mints Beans to `account` as a reward for calling {sunrise()}. */ function incentivize( address account, uint256 initialGasLeft, LibTransfer.To mode ) private returns (uint256) { // Number of blocks the sunrise is late by // Assumes that each block timestamp is exactly `C.BLOCK_LENGTH_SECONDS` apart. uint256 blocksLate = block .timestamp .sub(s.season.start.add(s.season.period.mul(s.season.current))) .div(C.BLOCK_LENGTH_SECONDS); // Read the Bean / Eth price calculated by the Minting Well. uint256 beanEthPrice = LibWell.getBeanTokenPriceFromTwaReserves(C.BEAN_ETH_WELL); // reset USD Token prices and TWA reserves in storage for all whitelisted Well LP Tokens. address[] memory whitelistedWells = LibWhitelistedTokens.getWhitelistedWellLpTokens(); for (uint256 i; i < whitelistedWells.length; i++) { LibWell.resetUsdTokenPriceForWell(whitelistedWells[i]); LibWell.resetTwaReservesForWell(whitelistedWells[i]); } uint256 incentiveAmount = LibIncentive.determineReward( initialGasLeft, blocksLate, beanEthPrice ); LibTransfer.mintToken(C.bean(), incentiveAmount, account, mode); emit LibIncentive.Incentivization(account, incentiveAmount); return incentiveAmount; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow, so we distribute return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a % b; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @title SignedSafeMath * @dev Signed math operations with safety checks that revert on error. */ library SignedSafeMath { int256 constant private _INT256_MIN = -2**255; /** * @dev Returns the multiplication of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(int256 a, int256 b) internal pure returns (int256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow"); int256 c = a * b; require(c / a == b, "SignedSafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two signed integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(int256 a, int256 b) internal pure returns (int256) { require(b != 0, "SignedSafeMath: division by zero"); require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow"); int256 c = a / b; return c; } /** * @dev Returns the subtraction of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(int256 a, int256 b) internal pure returns (int256) { int256 c = a - b; require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow"); return c; } /** * @dev Returns the addition of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(int256 a, int256 b) internal pure returns (int256) { int256 c = a + b; require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow"); return c; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "./IERC20.sol"; import "../../math/SafeMath.sol"; import "../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../math/SafeMath.sol"; /** * @title Counters * @author Matt Condon (@shrugs) * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number * of elements in a mapping, issuing ERC721 ids, or counting request ids. * * Include with `using Counters for Counters.Counter;` * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the {SafeMath} * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never * directly accessed. */ library Counters { using SafeMath for uint256; struct Counter { // This variable should never be directly accessed by users of the library: interactions must be restricted to // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add // this feature: see https://github.com/ethereum/solidity/issues/4637 uint256 _value; // default: 0 } function current(Counter storage counter) internal view returns (uint256) { return counter._value; } function increment(Counter storage counter) internal { // The {SafeMath} overflow check can be skipped here, see the comment at the top counter._value += 1; } function decrement(Counter storage counter) internal { counter._value = counter._value.sub(1); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits */ function toUint128(uint256 value) internal pure returns (uint128) { require(value < 2**128, "SafeCast: value doesn\'t fit in 128 bits"); return uint128(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits */ function toUint64(uint256 value) internal pure returns (uint64) { require(value < 2**64, "SafeCast: value doesn\'t fit in 64 bits"); return uint64(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits */ function toUint32(uint256 value) internal pure returns (uint32) { require(value < 2**32, "SafeCast: value doesn\'t fit in 32 bits"); return uint32(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits */ function toUint16(uint256 value) internal pure returns (uint16) { require(value < 2**16, "SafeCast: value doesn\'t fit in 16 bits"); return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits. */ function toUint8(uint256 value) internal pure returns (uint8) { require(value < 2**8, "SafeCast: value doesn\'t fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); return uint256(value); } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits * * _Available since v3.1._ */ function toInt128(int256 value) internal pure returns (int128) { require(value >= -2**127 && value < 2**127, "SafeCast: value doesn\'t fit in 128 bits"); return int128(value); } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits * * _Available since v3.1._ */ function toInt64(int256 value) internal pure returns (int64) { require(value >= -2**63 && value < 2**63, "SafeCast: value doesn\'t fit in 64 bits"); return int64(value); } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits * * _Available since v3.1._ */ function toInt32(int256 value) internal pure returns (int32) { require(value >= -2**31 && value < 2**31, "SafeCast: value doesn\'t fit in 32 bits"); return int32(value); } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits * * _Available since v3.1._ */ function toInt16(int256 value) internal pure returns (int16) { require(value >= -2**15 && value < 2**15, "SafeCast: value doesn\'t fit in 16 bits"); return int16(value); } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits. * * _Available since v3.1._ */ function toInt8(int256 value) internal pure returns (int8) { require(value >= -2**7 && value < 2**7, "SafeCast: value doesn\'t fit in 8 bits"); return int8(value); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. */ function toInt256(uint256 value) internal pure returns (int256) { require(value < 2**255, "SafeCast: value doesn't fit in an int256"); return int256(value); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; import './pool/IUniswapV3PoolImmutables.sol'; import './pool/IUniswapV3PoolState.sol'; import './pool/IUniswapV3PoolDerivedState.sol'; import './pool/IUniswapV3PoolActions.sol'; import './pool/IUniswapV3PoolOwnerActions.sol'; import './pool/IUniswapV3PoolEvents.sol'; /// @title The interface for a Uniswap V3 Pool /// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform /// to the ERC20 specification /// @dev The pool interface is broken up into many smaller pieces interface IUniswapV3Pool is IUniswapV3PoolImmutables, IUniswapV3PoolState, IUniswapV3PoolDerivedState, IUniswapV3PoolActions, IUniswapV3PoolOwnerActions, IUniswapV3PoolEvents { }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Permissionless pool actions /// @notice Contains pool methods that can be called by anyone interface IUniswapV3PoolActions { /// @notice Sets the initial price for the pool /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96 function initialize(uint160 sqrtPriceX96) external; /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends /// on tickLower, tickUpper, the amount of liquidity, and the current price. /// @param recipient The address for which the liquidity will be created /// @param tickLower The lower tick of the position in which to add liquidity /// @param tickUpper The upper tick of the position in which to add liquidity /// @param amount The amount of liquidity to mint /// @param data Any data that should be passed through to the callback /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback function mint( address recipient, int24 tickLower, int24 tickUpper, uint128 amount, bytes calldata data ) external returns (uint256 amount0, uint256 amount1); /// @notice Collects tokens owed to a position /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity. /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity. /// @param recipient The address which should receive the fees collected /// @param tickLower The lower tick of the position for which to collect fees /// @param tickUpper The upper tick of the position for which to collect fees /// @param amount0Requested How much token0 should be withdrawn from the fees owed /// @param amount1Requested How much token1 should be withdrawn from the fees owed /// @return amount0 The amount of fees collected in token0 /// @return amount1 The amount of fees collected in token1 function collect( address recipient, int24 tickLower, int24 tickUpper, uint128 amount0Requested, uint128 amount1Requested ) external returns (uint128 amount0, uint128 amount1); /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0 /// @dev Fees must be collected separately via a call to #collect /// @param tickLower The lower tick of the position for which to burn liquidity /// @param tickUpper The upper tick of the position for which to burn liquidity /// @param amount How much liquidity to burn /// @return amount0 The amount of token0 sent to the recipient /// @return amount1 The amount of token1 sent to the recipient function burn( int24 tickLower, int24 tickUpper, uint128 amount ) external returns (uint256 amount0, uint256 amount1); /// @notice Swap token0 for token1, or token1 for token0 /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback /// @param recipient The address to receive the output of the swap /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0 /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative) /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this /// value after the swap. If one for zero, the price cannot be greater than this value after the swap /// @param data Any data to be passed through to the callback /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive function swap( address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, bytes calldata data ) external returns (int256 amount0, int256 amount1); /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback /// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback /// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling /// with 0 amount{0,1} and sending the donation amount(s) from the callback /// @param recipient The address which will receive the token0 and token1 amounts /// @param amount0 The amount of token0 to send /// @param amount1 The amount of token1 to send /// @param data Any data to be passed through to the callback function flash( address recipient, uint256 amount0, uint256 amount1, bytes calldata data ) external; /// @notice Increase the maximum number of price and liquidity observations that this pool will store /// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to /// the input observationCardinalityNext. /// @param observationCardinalityNext The desired minimum number of observations for the pool to store function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Pool state that is not stored /// @notice Contains view functions to provide information about the pool that is computed rather than stored on the /// blockchain. The functions here may have variable gas costs. interface IUniswapV3PoolDerivedState { /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick, /// you must call it with secondsAgos = [3600, 0]. /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio. /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block /// timestamp function observe(uint32[] calldata secondsAgos) external view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s); /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed. /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first /// snapshot is taken and the second snapshot is taken. /// @param tickLower The lower tick of the range /// @param tickUpper The upper tick of the range /// @return tickCumulativeInside The snapshot of the tick accumulator for the range /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range /// @return secondsInside The snapshot of seconds per liquidity for the range function snapshotCumulativesInside(int24 tickLower, int24 tickUpper) external view returns ( int56 tickCumulativeInside, uint160 secondsPerLiquidityInsideX128, uint32 secondsInside ); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Events emitted by a pool /// @notice Contains all events emitted by the pool interface IUniswapV3PoolEvents { /// @notice Emitted exactly once by a pool when #initialize is first called on the pool /// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize /// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96 /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool event Initialize(uint160 sqrtPriceX96, int24 tick); /// @notice Emitted when liquidity is minted for a given position /// @param sender The address that minted the liquidity /// @param owner The owner of the position and recipient of any minted liquidity /// @param tickLower The lower tick of the position /// @param tickUpper The upper tick of the position /// @param amount The amount of liquidity minted to the position range /// @param amount0 How much token0 was required for the minted liquidity /// @param amount1 How much token1 was required for the minted liquidity event Mint( address sender, address indexed owner, int24 indexed tickLower, int24 indexed tickUpper, uint128 amount, uint256 amount0, uint256 amount1 ); /// @notice Emitted when fees are collected by the owner of a position /// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees /// @param owner The owner of the position for which fees are collected /// @param tickLower The lower tick of the position /// @param tickUpper The upper tick of the position /// @param amount0 The amount of token0 fees collected /// @param amount1 The amount of token1 fees collected event Collect( address indexed owner, address recipient, int24 indexed tickLower, int24 indexed tickUpper, uint128 amount0, uint128 amount1 ); /// @notice Emitted when a position's liquidity is removed /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect /// @param owner The owner of the position for which liquidity is removed /// @param tickLower The lower tick of the position /// @param tickUpper The upper tick of the position /// @param amount The amount of liquidity to remove /// @param amount0 The amount of token0 withdrawn /// @param amount1 The amount of token1 withdrawn event Burn( address indexed owner, int24 indexed tickLower, int24 indexed tickUpper, uint128 amount, uint256 amount0, uint256 amount1 ); /// @notice Emitted by the pool for any swaps between token0 and token1 /// @param sender The address that initiated the swap call, and that received the callback /// @param recipient The address that received the output of the swap /// @param amount0 The delta of the token0 balance of the pool /// @param amount1 The delta of the token1 balance of the pool /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96 /// @param liquidity The liquidity of the pool after the swap /// @param tick The log base 1.0001 of price of the pool after the swap event Swap( address indexed sender, address indexed recipient, int256 amount0, int256 amount1, uint160 sqrtPriceX96, uint128 liquidity, int24 tick ); /// @notice Emitted by the pool for any flashes of token0/token1 /// @param sender The address that initiated the swap call, and that received the callback /// @param recipient The address that received the tokens from flash /// @param amount0 The amount of token0 that was flashed /// @param amount1 The amount of token1 that was flashed /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee event Flash( address indexed sender, address indexed recipient, uint256 amount0, uint256 amount1, uint256 paid0, uint256 paid1 ); /// @notice Emitted by the pool for increases to the number of observations that can be stored /// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index /// just before a mint/swap/burn. /// @param observationCardinalityNextOld The previous value of the next observation cardinality /// @param observationCardinalityNextNew The updated value of the next observation cardinality event IncreaseObservationCardinalityNext( uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew ); /// @notice Emitted when the protocol fee is changed by the pool /// @param feeProtocol0Old The previous value of the token0 protocol fee /// @param feeProtocol1Old The previous value of the token1 protocol fee /// @param feeProtocol0New The updated value of the token0 protocol fee /// @param feeProtocol1New The updated value of the token1 protocol fee event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New); /// @notice Emitted when the collected protocol fees are withdrawn by the factory owner /// @param sender The address that collects the protocol fees /// @param recipient The address that receives the collected protocol fees /// @param amount0 The amount of token0 protocol fees that is withdrawn /// @param amount0 The amount of token1 protocol fees that is withdrawn event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Pool state that never changes /// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values interface IUniswapV3PoolImmutables { /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface /// @return The contract address function factory() external view returns (address); /// @notice The first of the two tokens of the pool, sorted by address /// @return The token contract address function token0() external view returns (address); /// @notice The second of the two tokens of the pool, sorted by address /// @return The token contract address function token1() external view returns (address); /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6 /// @return The fee function fee() external view returns (uint24); /// @notice The pool tick spacing /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ... /// This value is an int24 to avoid casting even though it is always positive. /// @return The tick spacing function tickSpacing() external view returns (int24); /// @notice The maximum amount of position liquidity that can use any tick in the range /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool /// @return The max amount of liquidity per tick function maxLiquidityPerTick() external view returns (uint128); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Permissioned pool actions /// @notice Contains pool methods that may only be called by the factory owner interface IUniswapV3PoolOwnerActions { /// @notice Set the denominator of the protocol's % share of the fees /// @param feeProtocol0 new protocol fee for token0 of the pool /// @param feeProtocol1 new protocol fee for token1 of the pool function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external; /// @notice Collect the protocol fee accrued to the pool /// @param recipient The address to which collected protocol fees should be sent /// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1 /// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0 /// @return amount0 The protocol fee collected in token0 /// @return amount1 The protocol fee collected in token1 function collectProtocol( address recipient, uint128 amount0Requested, uint128 amount1Requested ) external returns (uint128 amount0, uint128 amount1); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title Pool state that can change /// @notice These methods compose the pool's state, and can change with any frequency including multiple times /// per transaction interface IUniswapV3PoolState { /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas /// when accessed externally. /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value /// tick The current tick of the pool, i.e. according to the last tick transition that was run. /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick /// boundary. /// observationIndex The index of the last oracle observation that was written, /// observationCardinality The current maximum number of observations stored in the pool, /// observationCardinalityNext The next maximum number of observations, to be updated when the observation. /// feeProtocol The protocol fee for both tokens of the pool. /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0 /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee. /// unlocked Whether the pool is currently locked to reentrancy function slot0() external view returns ( uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint8 feeProtocol, bool unlocked ); /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool /// @dev This value can overflow the uint256 function feeGrowthGlobal0X128() external view returns (uint256); /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool /// @dev This value can overflow the uint256 function feeGrowthGlobal1X128() external view returns (uint256); /// @notice The amounts of token0 and token1 that are owed to the protocol /// @dev Protocol fees will never exceed uint128 max in either token function protocolFees() external view returns (uint128 token0, uint128 token1); /// @notice The currently in range liquidity available to the pool /// @dev This value has no relationship to the total liquidity across all ticks function liquidity() external view returns (uint128); /// @notice Look up information about a specific tick in the pool /// @param tick The tick to look up /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or /// tick upper, /// liquidityNet how much liquidity changes when the pool price crosses the tick, /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0, /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1, /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick, /// secondsOutside the seconds spent on the other side of the tick from the current tick, /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false. /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0. /// In addition, these values are only relative and must be used only in comparison to previous snapshots for /// a specific position. function ticks(int24 tick) external view returns ( uint128 liquidityGross, int128 liquidityNet, uint256 feeGrowthOutside0X128, uint256 feeGrowthOutside1X128, int56 tickCumulativeOutside, uint160 secondsPerLiquidityOutsideX128, uint32 secondsOutside, bool initialized ); /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information function tickBitmap(int16 wordPosition) external view returns (uint256); /// @notice Returns the information about a position by the position's key /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper /// @return _liquidity The amount of liquidity in the position, /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke, /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke, /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke, /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke function positions(bytes32 key) external view returns ( uint128 _liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, uint128 tokensOwed0, uint128 tokensOwed1 ); /// @notice Returns data about a specific observation index /// @param index The element of the observations array to fetch /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time /// ago, rather than at a specific index in the array. /// @return blockTimestamp The timestamp of the observation, /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp, /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp, /// Returns initialized whether the observation has been initialized and the values are safe to use function observations(uint256 index) external view returns ( uint32 blockTimestamp, int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128, bool initialized ); }
// SPDX-License-Identifier: MIT pragma solidity >=0.4.0 <0.8.0; /// @title Contains 512-bit math functions /// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision /// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits library FullMath { /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 /// @param a The multiplicand /// @param b The multiplier /// @param denominator The divisor /// @return result The 256-bit result /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv function mulDiv( uint256 a, uint256 b, uint256 denominator ) internal pure returns (uint256 result) { // 512-bit multiply [prod1 prod0] = a * b // Compute the product mod 2**256 and mod 2**256 - 1 // then use the Chinese Remainder Theorem to reconstruct // the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2**256 + prod0 uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(a, b, not(0)) prod0 := mul(a, b) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division if (prod1 == 0) { require(denominator > 0); assembly { result := div(prod0, denominator) } return result; } // Make sure the result is less than 2**256. // Also prevents denominator == 0 require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0] // Compute remainder using mulmod uint256 remainder; assembly { remainder := mulmod(a, b, denominator) } // Subtract 256 bit number from 512 bit number assembly { prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator // Compute largest power of two divisor of denominator. // Always >= 1. uint256 twos = -denominator & denominator; // Divide denominator by power of two assembly { denominator := div(denominator, twos) } // Divide [prod1 prod0] by the factors of two assembly { prod0 := div(prod0, twos) } // Shift in bits from prod1 into prod0. For this we need // to flip `twos` such that it is 2**256 / twos. // If twos is zero, then it becomes one assembly { twos := add(div(sub(0, twos), twos), 1) } prod0 |= prod1 * twos; // Invert denominator mod 2**256 // Now that denominator is an odd number, it has an inverse // modulo 2**256 such that denominator * inv = 1 mod 2**256. // Compute the inverse by starting with a seed that is correct // correct for four bits. That is, denominator * inv = 1 mod 2**4 uint256 inv = (3 * denominator) ^ 2; // Now use Newton-Raphson iteration to improve the precision. // Thanks to Hensel's lifting lemma, this also works in modular // arithmetic, doubling the correct bits in each step. inv *= 2 - denominator * inv; // inverse mod 2**8 inv *= 2 - denominator * inv; // inverse mod 2**16 inv *= 2 - denominator * inv; // inverse mod 2**32 inv *= 2 - denominator * inv; // inverse mod 2**64 inv *= 2 - denominator * inv; // inverse mod 2**128 inv *= 2 - denominator * inv; // inverse mod 2**256 // Because the division is now exact we can divide by multiplying // with the modular inverse of denominator. This will give us the // correct result modulo 2**256. Since the precoditions guarantee // that the outcome is less than 2**256, this is the final result. // We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inv; return result; } /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 /// @param a The multiplicand /// @param b The multiplier /// @param denominator The divisor /// @return result The 256-bit result function mulDivRoundingUp( uint256 a, uint256 b, uint256 denominator ) internal pure returns (uint256 result) { result = mulDiv(a, b, denominator); if (mulmod(a, b, denominator) > 0) { require(result < type(uint256).max); result++; } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0 <0.8.0; /// @title Math library for computing sqrt prices from ticks and vice versa /// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports /// prices between 2**-128 and 2**128 library TickMath { /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128 int24 internal constant MIN_TICK = -887272; /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128 int24 internal constant MAX_TICK = -MIN_TICK; /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK) uint160 internal constant MIN_SQRT_RATIO = 4295128739; /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK) uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342; /// @notice Calculates sqrt(1.0001^tick) * 2^96 /// @dev Throws if |tick| > max tick /// @param tick The input tick for the above formula /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0) /// at the given tick function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) { uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick)); require(absTick <= uint256(MAX_TICK), 'T'); uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000; if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128; if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128; if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128; if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128; if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128; if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128; if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128; if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128; if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128; if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128; if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128; if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128; if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128; if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128; if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128; if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128; if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128; if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128; if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128; if (tick > 0) ratio = type(uint256).max / ratio; // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96. // we then downcast because we know the result always fits within 160 bits due to our tick input constraint // we round up in the division so getTickAtSqrtRatio of the output price is always consistent sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1)); } /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may /// ever return. /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96 /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) { // second inequality must be < because the price can never reach the price at the max tick require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, 'R'); uint256 ratio = uint256(sqrtPriceX96) << 32; uint256 r = ratio; uint256 msb = 0; assembly { let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) msb := or(msb, f) r := shr(f, r) } assembly { let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF)) msb := or(msb, f) r := shr(f, r) } assembly { let f := shl(5, gt(r, 0xFFFFFFFF)) msb := or(msb, f) r := shr(f, r) } assembly { let f := shl(4, gt(r, 0xFFFF)) msb := or(msb, f) r := shr(f, r) } assembly { let f := shl(3, gt(r, 0xFF)) msb := or(msb, f) r := shr(f, r) } assembly { let f := shl(2, gt(r, 0xF)) msb := or(msb, f) r := shr(f, r) } assembly { let f := shl(1, gt(r, 0x3)) msb := or(msb, f) r := shr(f, r) } assembly { let f := gt(r, 0x1) msb := or(msb, f) } if (msb >= 128) r = ratio >> (msb - 127); else r = ratio << (127 - msb); int256 log_2 = (int256(msb) - 128) << 64; assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(63, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(62, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(61, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(60, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(59, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(58, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(57, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(56, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(55, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(54, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(53, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(52, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(51, f)) r := shr(f, r) } assembly { r := shr(127, mul(r, r)) let f := shr(128, r) log_2 := or(log_2, shl(50, f)) } int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128); int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128); tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow; } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0 <0.8.0; import '@uniswap/v3-core/contracts/libraries/FullMath.sol'; import '@uniswap/v3-core/contracts/libraries/TickMath.sol'; import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; /// @title Oracle library /// @notice Provides functions to integrate with V3 pool oracle library OracleLibrary { /// @notice Calculates time-weighted means of tick and liquidity for a given Uniswap V3 pool /// @param pool Address of the pool that we want to observe /// @param secondsAgo Number of seconds in the past from which to calculate the time-weighted means /// @return arithmeticMeanTick The arithmetic mean tick from (block.timestamp - secondsAgo) to block.timestamp /// @return harmonicMeanLiquidity The harmonic mean liquidity from (block.timestamp - secondsAgo) to block.timestamp function consult(address pool, uint32 secondsAgo) internal view returns (int24 arithmeticMeanTick, uint128 harmonicMeanLiquidity) { require(secondsAgo != 0, 'BP'); uint32[] memory secondsAgos = new uint32[](2); secondsAgos[0] = secondsAgo; secondsAgos[1] = 0; (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) = IUniswapV3Pool(pool).observe(secondsAgos); int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0]; uint160 secondsPerLiquidityCumulativesDelta = secondsPerLiquidityCumulativeX128s[1] - secondsPerLiquidityCumulativeX128s[0]; arithmeticMeanTick = int24(tickCumulativesDelta / secondsAgo); // Always round to negative infinity if (tickCumulativesDelta < 0 && (tickCumulativesDelta % secondsAgo != 0)) arithmeticMeanTick--; // We are multiplying here instead of shifting to ensure that harmonicMeanLiquidity doesn't overflow uint128 uint192 secondsAgoX160 = uint192(secondsAgo) * type(uint160).max; harmonicMeanLiquidity = uint128(secondsAgoX160 / (uint192(secondsPerLiquidityCumulativesDelta) << 32)); } /// @notice Given a tick and a token amount, calculates the amount of token received in exchange /// @param tick Tick value used to calculate the quote /// @param baseAmount Amount of token to be converted /// @param baseToken Address of an ERC20 token contract used as the baseAmount denomination /// @param quoteToken Address of an ERC20 token contract used as the quoteAmount denomination /// @return quoteAmount Amount of quoteToken received for baseAmount of baseToken function getQuoteAtTick( int24 tick, uint128 baseAmount, address baseToken, address quoteToken ) internal pure returns (uint256 quoteAmount) { uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(tick); // Calculate quoteAmount with better precision if it doesn't overflow when multiplied by itself if (sqrtRatioX96 <= type(uint128).max) { uint256 ratioX192 = uint256(sqrtRatioX96) * sqrtRatioX96; quoteAmount = baseToken < quoteToken ? FullMath.mulDiv(ratioX192, baseAmount, 1 << 192) : FullMath.mulDiv(1 << 192, baseAmount, ratioX192); } else { uint256 ratioX128 = FullMath.mulDiv(sqrtRatioX96, sqrtRatioX96, 1 << 64); quoteAmount = baseToken < quoteToken ? FullMath.mulDiv(ratioX128, baseAmount, 1 << 128) : FullMath.mulDiv(1 << 128, baseAmount, ratioX128); } } /// @notice Given a pool, it returns the number of seconds ago of the oldest stored observation /// @param pool Address of Uniswap V3 pool that we want to observe /// @return secondsAgo The number of seconds ago of the oldest observation stored for the pool function getOldestObservationSecondsAgo(address pool) internal view returns (uint32 secondsAgo) { (, , uint16 observationIndex, uint16 observationCardinality, , , ) = IUniswapV3Pool(pool).slot0(); require(observationCardinality > 0, 'NI'); (uint32 observationTimestamp, , , bool initialized) = IUniswapV3Pool(pool).observations((observationIndex + 1) % observationCardinality); // The next index might not be initialized if the cardinality is in the process of increasing // In this case the oldest observation is always in index 0 if (!initialized) { (observationTimestamp, , , ) = IUniswapV3Pool(pool).observations(0); } secondsAgo = uint32(block.timestamp) - observationTimestamp; } /// @notice Given a pool, it returns the tick value as of the start of the current block /// @param pool Address of Uniswap V3 pool /// @return The tick that the pool was in at the start of the current block function getBlockStartingTickAndLiquidity(address pool) internal view returns (int24, uint128) { (, int24 tick, uint16 observationIndex, uint16 observationCardinality, , , ) = IUniswapV3Pool(pool).slot0(); // 2 observations are needed to reliably calculate the block starting tick require(observationCardinality > 1, 'NEO'); // If the latest observation occurred in the past, then no tick-changing trades have happened in this block // therefore the tick in `slot0` is the same as at the beginning of the current block. // We don't need to check if this observation is initialized - it is guaranteed to be. (uint32 observationTimestamp, int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128, ) = IUniswapV3Pool(pool).observations(observationIndex); if (observationTimestamp != uint32(block.timestamp)) { return (tick, IUniswapV3Pool(pool).liquidity()); } uint256 prevIndex = (uint256(observationIndex) + observationCardinality - 1) % observationCardinality; ( uint32 prevObservationTimestamp, int56 prevTickCumulative, uint160 prevSecondsPerLiquidityCumulativeX128, bool prevInitialized ) = IUniswapV3Pool(pool).observations(prevIndex); require(prevInitialized, 'ONI'); uint32 delta = observationTimestamp - prevObservationTimestamp; tick = int24((tickCumulative - prevTickCumulative) / delta); uint128 liquidity = uint128( (uint192(delta) * type(uint160).max) / (uint192(secondsPerLiquidityCumulativeX128 - prevSecondsPerLiquidityCumulativeX128) << 32) ); return (tick, liquidity); } /// @notice Information for calculating a weighted arithmetic mean tick struct WeightedTickData { int24 tick; uint128 weight; } /// @notice Given an array of ticks and weights, calculates the weighted arithmetic mean tick /// @param weightedTickData An array of ticks and weights /// @return weightedArithmeticMeanTick The weighted arithmetic mean tick /// @dev Each entry of `weightedTickData` should represents ticks from pools with the same underlying pool tokens. If they do not, /// extreme care must be taken to ensure that ticks are comparable (including decimal differences). /// @dev Note that the weighted arithmetic mean tick corresponds to the weighted geometric mean price. function getWeightedArithmeticMeanTick(WeightedTickData[] memory weightedTickData) internal pure returns (int24 weightedArithmeticMeanTick) { // Accumulates the sum of products between each tick and its weight int256 numerator; // Accumulates the sum of the weights uint256 denominator; // Products fit in 152 bits, so it would take an array of length ~2**104 to overflow this logic for (uint256 i; i < weightedTickData.length; i++) { numerator += weightedTickData[i].tick * int256(weightedTickData[i].weight); denominator += weightedTickData[i].weight; } weightedArithmeticMeanTick = int24(numerator / int256(denominator)); // Always round to negative infinity if (numerator < 0 && (numerator % int256(denominator) != 0)) weightedArithmeticMeanTick--; } /// @notice Returns the "synthetic" tick which represents the price of the first entry in `tokens` in terms of the last /// @dev Useful for calculating relative prices along routes. /// @dev There must be one tick for each pairwise set of tokens. /// @param tokens The token contract addresses /// @param ticks The ticks, representing the price of each token pair in `tokens` /// @return syntheticTick The synthetic tick, representing the relative price of the outermost tokens in `tokens` function getChainedPrice(address[] memory tokens, int24[] memory ticks) internal pure returns (int256 syntheticTick) { require(tokens.length - 1 == ticks.length, 'DL'); for (uint256 i = 1; i <= ticks.length; i++) { // check the tokens for address sort order, then accumulate the // ticks into the running synthetic tick, ensuring that intermediate tokens "cancel out" tokens[i - 1] < tokens[i] ? syntheticTick += ticks[i - 1] : syntheticTick -= ticks[i - 1]; } } }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import "../interfaces/IDiamondCut.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; /** * @title Account * @author Publius * @notice Stores Farmer-level Beanstalk state. * @dev {Account.State} is the primary struct that is referenced from {Storage.State}. * All other structs in {Account} are referenced in {Account.State}. Each unique * Ethereum address is a Farmer. */ contract Account { /** * @notice Stores a Farmer's Plots and Pod allowances. * @param plots A Farmer's Plots. Maps from Plot index to Pod amount. * @param podAllowances An allowance mapping for Pods similar to that of the ERC-20 standard. Maps from spender address to allowance amount. */ struct Field { mapping(uint256 => uint256) plots; mapping(address => uint256) podAllowances; } /** * @notice Stores a Farmer's Deposits and Seeds per Deposit, and formerly stored Withdrawals. * @param withdrawals DEPRECATED: Silo V1 Withdrawals are no longer referenced. * @param deposits Unripe Bean/LP Deposits (previously Bean/LP Deposits). * @param depositSeeds BDV of Unripe LP Deposits / 4 (previously # of Seeds in corresponding LP Deposit). */ struct AssetSilo { mapping(uint32 => uint256) withdrawals; mapping(uint32 => uint256) deposits; mapping(uint32 => uint256) depositSeeds; } /** * @notice Represents a Deposit of a given Token in the Silo at a given Season. * @param amount The amount of Tokens in the Deposit. * @param bdv The Bean-denominated value of the total amount of Tokens in the Deposit. * @dev `amount` and `bdv` are packed as uint128 to save gas. */ struct Deposit { uint128 amount; // ───┐ 16 uint128 bdv; // ──────┘ 16 (32/32) } /** * @notice Stores a Farmer's Stalk and Seeds balances. * @param stalk Balance of the Farmer's Stalk. * @param seeds DEPRECATED – Balance of the Farmer's Seeds. Seeds are no longer referenced as of Silo V3. */ struct Silo { uint256 stalk; uint256 seeds; } /** * @notice Stores a Farmer's germinating stalk. * @param odd - stalk from assets deposited in odd seasons. * @param even - stalk from assets deposited in even seasons. */ struct FarmerGerminatingStalk { uint128 odd; uint128 even; } /** * @notice This struct stores the mow status for each Silo-able token, for each farmer. * This gets updated each time a farmer mows, or adds/removes deposits. * @param lastStem The last cumulative grown stalk per bdv index at which the farmer mowed. * @param bdv The bdv of all of a farmer's deposits of this token type. * */ struct MowStatus { int96 lastStem; // ───┐ 12 uint128 bdv; // ──────┘ 16 (28/32) } /** * @notice Stores a Farmer's Season of Plenty (SOP) balances. * @param roots The number of Roots a Farmer had when it started Raining. * @param plentyPerRoot The global Plenty Per Root index at the last time a Farmer updated their Silo. * @param plenty The balance of a Farmer's plenty. Plenty can be claimed directly for 3CRV. */ struct SeasonOfPlenty { uint256 roots; uint256 plentyPerRoot; uint256 plenty; } /** * @notice Defines the state object for a Farmer. * @param field A Farmer's Field storage. * @param bean A Farmer's Unripe Bean Deposits only as a result of Replant (previously held the V1 Silo Deposits/Withdrawals for Beans). * @param lp A Farmer's Unripe LP Deposits as a result of Replant of BEAN:ETH Uniswap v2 LP Tokens (previously held the V1 Silo Deposits/Withdrawals for BEAN:ETH Uniswap v2 LP Tokens). * @param s A Farmer's Silo storage. * @param deprecated_votedUntil DEPRECATED – Replant removed on-chain governance including the ability to vote on BIPs. * @param lastUpdate The Season in which the Farmer last updated their Silo. * @param lastSop The last Season that a SOP occured at the time the Farmer last updated their Silo. * @param lastRain The last Season that it started Raining at the time the Farmer last updated their Silo. * @param deprecated_deltaRoots DEPRECATED – BIP-39 introduced germination. * @param deprecated_lastSIs DEPRECATED – In Silo V1.2, the Silo reward mechanism was updated to no longer need to store the number of the Supply Increases at the time the Farmer last updated their Silo. * @param deprecated_proposedUntil DEPRECATED – Replant removed on-chain governance including the ability to propose BIPs. * @param deprecated_sop DEPRECATED – Replant reset the Season of Plenty mechanism * @param roots A Farmer's Root balance. * @param deprecated_wrappedBeans DEPRECATED – Replant generalized Internal Balances. Wrapped Beans are now stored at the AppStorage level. * @param legacyV2Deposits DEPRECATED - SiloV2 was retired in favor of Silo V3. A Farmer's Silo Deposits stored as a map from Token address to Season of Deposit to Deposit. * @param withdrawals Withdraws were removed in zero withdraw upgrade - A Farmer's Withdrawals from the Silo stored as a map from Token address to Season the Withdrawal becomes Claimable to Withdrawn amount of Tokens. * @param sop A Farmer's Season of Plenty storage. * @param depositAllowances A mapping of `spender => Silo token address => amount`. * @param tokenAllowances Internal balance token allowances. * @param depositPermitNonces A Farmer's current deposit permit nonce * @param tokenPermitNonces A Farmer's current token permit nonce * @param legacyV3Deposits DEPRECATED: Silo V3 deposits. Deprecated in favor of SiloV3.1 mapping from depositId to Deposit. * @param mowStatuses A mapping of Silo-able token address to MowStatus. * @param isApprovedForAll A mapping of ERC1155 operator to approved status. ERC1155 compatability. * @param farmerGerminating A Farmer's germinating stalk. Seperated into odd and even stalk. * @param deposits SiloV3.1 deposits. A mapping from depositId to Deposit. SiloV3.1 introduces greater precision for deposits. */ struct State { Field field; // A Farmer's Field storage. /* * @dev (Silo V1) A Farmer's Unripe Bean Deposits only as a result of Replant * * Previously held the V1 Silo Deposits/Withdrawals for Beans. * NOTE: While the Silo V1 format is now deprecated, this storage slot is used for gas * efficiency to store Unripe BEAN deposits. See {LibUnripeSilo} for more. */ AssetSilo bean; /* * @dev (Silo V1) Unripe LP Deposits as a result of Replant. * * Previously held the V1 Silo Deposits/Withdrawals for BEAN:ETH Uniswap v2 LP Tokens. * * BEAN:3CRV and BEAN:LUSD tokens prior to Replant were stored in the Silo V2 * format in the `s.a[account].legacyV2Deposits` mapping. * * NOTE: While the Silo V1 format is now deprecated, unmigrated Silo V1 deposits are still * stored in this storage slot. See {LibUnripeSilo} for more. * */ AssetSilo lp; /* * @dev Holds Silo specific state for each account. */ Silo s; uint32 votedUntil; // DEPRECATED – Replant removed on-chain governance including the ability to vote on BIPs. uint32 lastUpdate; // The Season in which the Farmer last updated their Silo. uint32 lastSop; // The last Season that a SOP occured at the time the Farmer last updated their Silo. uint32 lastRain; // The last Season that it started Raining at the time the Farmer last updated their Silo. uint128 deprecated_deltaRoots; // DEPRECATED - BIP-39 introduced germination. SeasonOfPlenty deprecated; // DEPRECATED – Replant reset the Season of Plenty mechanism uint256 roots; // A Farmer's Root balance. uint256 deprecated_wrappedBeans; // DEPRECATED – Replant generalized Internal Balances. Wrapped Beans are now stored at the AppStorage level. mapping(address => mapping(uint32 => Deposit)) legacyV2Deposits; // Legacy Silo V2 Deposits stored as a map from Token address to Season of Deposit to Deposit. NOTE: While the Silo V2 format is now deprecated, unmigrated Silo V2 deposits are still stored in this mapping. mapping(address => mapping(uint32 => uint256)) withdrawals; // Zero withdraw eliminates a need for withdraw mapping, but is kept for legacy SeasonOfPlenty sop; // A Farmer's Season Of Plenty storage. mapping(address => mapping(address => uint256)) depositAllowances; // Spender => Silo Token mapping(address => mapping(IERC20 => uint256)) tokenAllowances; // Token allowances uint256 depositPermitNonces; // A Farmer's current deposit permit nonce uint256 tokenPermitNonces; // A Farmer's current token permit nonce mapping(uint256 => Deposit) legacyV3Deposits; // NOTE: Legacy SiloV3 Deposits stored as a map from uint256 to Deposit. This is an concat of the token address and the CGSPBDV for a ERC20 deposit. mapping(address => MowStatus) mowStatuses; // Store a MowStatus for each Whitelisted Silo token mapping(address => bool) isApprovedForAll; // ERC1155 isApprovedForAll mapping // Germination FarmerGerminatingStalk farmerGerminating; // A Farmer's germinating stalk. // Silo v3.1 mapping(uint256 => Deposit) deposits; // Silo v3.1 Deposits stored as a map from uint256 to Deposit. This is an concat of the token address and the stem for a ERC20 deposit. } } /** * @title Storage * @author Publius * @notice Stores system-level Beanstalk state. */ contract Storage { /** * @notice DEPRECATED: System-level contract addresses. * @dev After Replant, Beanstalk stores Token addresses as constants to save gas. */ struct Contracts { address bean; address pair; address pegPair; address weth; } /** * @notice System-level Field state variables. * @param soil The number of Soil currently available. Adjusted during {Sun.stepSun}. * @param beanSown The number of Bean sown within the current Season. Reset during {Weather.calcCaseId}. * @param pods The pod index; the total number of Pods ever minted. * @param harvested The harvested index; the total number of Pods that have ever been Harvested. * @param harvestable The harvestable index; the total number of Pods that have ever been Harvestable. Included previously Harvested Beans. */ struct Field { uint128 soil; // ──────┐ 16 uint128 beanSown; // ──┘ 16 (32/32) uint256 pods; uint256 harvested; uint256 harvestable; } /** * @notice DEPRECATED: Contained data about each BIP (Beanstalk Improvement Proposal). * @dev Replant moved governance off-chain. This struct is left for future reference. * */ struct Bip { address proposer; // ───┐ 20 uint32 start; // │ 4 (24) uint32 period; // │ 4 (28) bool executed; // ──────┘ 1 (29/32) int pauseOrUnpause; uint128 timestamp; uint256 roots; uint256 endTotalRoots; } /** * @notice DEPRECATED: Contained data for the DiamondCut associated with each BIP. * @dev Replant moved governance off-chain. This struct is left for future reference. * @dev {Storage.DiamondCut} stored DiamondCut-related data for each {Bip}. */ struct DiamondCut { IDiamondCut.FacetCut[] diamondCut; address initAddress; bytes initData; } /** * @notice DEPRECATED: Contained all governance-related data, including a list of BIPs, votes for each BIP, and the DiamondCut needed to execute each BIP. * @dev Replant moved governance off-chain. This struct is left for future reference. * @dev {Storage.Governance} stored all BIPs and Farmer voting information. */ struct Governance { uint32[] activeBips; uint32 bipIndex; mapping(uint32 => DiamondCut) diamondCuts; mapping(uint32 => mapping(address => bool)) voted; mapping(uint32 => Bip) bips; } /** * @notice System-level Silo state; contains deposit and withdrawal data for a particular whitelisted Token. * @param deposited The total amount of this Token currently Deposited in the Silo. * @param depositedBdv The total bdv of this Token currently Deposited in the Silo. * @param withdrawn The total amount of this Token currently Withdrawn From the Silo. * @dev {Storage.State} contains a mapping from Token address => AssetSilo. * Currently, the bdv of deposits are asynchronous, and require an on-chain transaction to update. * Thus, the total bdv of deposits cannot be calculated, and must be stored and updated upon a bdv change. * * * Note that "Withdrawn" refers to the amount of Tokens that have been Withdrawn * but not yet Claimed. This will be removed in a future BIP. */ struct AssetSilo { uint128 deposited; uint128 depositedBdv; uint256 withdrawn; } /** * @notice Whitelist Status a token that has been Whitelisted before. * @param token the address of the token. * @param a whether the address is whitelisted. * @param isWhitelistedLp whether the address is a whitelisted LP token. * @param isWhitelistedWell whether the address is a whitelisted Well token. */ struct WhitelistStatus { address token; bool isWhitelisted; bool isWhitelistedLp; bool isWhitelistedWell; } /** * @notice System-level Silo state variables. * @param stalk The total amount of active Stalk (including Earned Stalk, excluding Grown Stalk). * @param deprecated_seeds DEPRECATED: The total amount of active Seeds (excluding Earned Seeds). * @dev seeds are no longer used internally. Balance is wiped to 0 from the mayflower update. see {mowAndMigrate}. * @param roots The total amount of Roots. */ struct Silo { uint256 stalk; uint256 deprecated_seeds; uint256 roots; } /** * @notice System-level Curve Metapool Oracle state variables. * @param initialized True if the Oracle has been initialzed. It needs to be initialized on Deployment and re-initialized each Unpause. * @param startSeason The Season the Oracle started minting. Used to ramp up delta b when oracle is first added. * @param balances The cumulative reserve balances of the pool at the start of the Season (used for computing time weighted average delta b). * @param timestamp DEPRECATED: The timestamp of the start of the current Season. `LibCurveMinting` now uses `s.season.timestamp` instead of storing its own for gas efficiency purposes. * @dev Currently refers to the time weighted average deltaB calculated from the BEAN:3CRV pool. */ struct CurveMetapoolOracle { bool initialized; // ────┐ 1 uint32 startSeason; // ──┘ 4 (5/32) uint256[2] balances; uint256 timestamp; } /** * @notice System-level Rain balances. Rain occurs when P > 1 and the Pod Rate Excessively Low. * @dev The `raining` storage variable is stored in the Season section for a gas efficient read operation. * @param deprecated Previously held Rain start and Rain status variables. Now moved to Season struct for gas efficiency. * @param pods The number of Pods when it last started Raining. * @param roots The number of Roots when it last started Raining. */ struct Rain { uint256 deprecated; uint256 pods; uint256 roots; } /** * @notice System-level Season state variables. * @param current The current Season in Beanstalk. * @param lastSop The Season in which the most recent consecutive series of Seasons of Plenty started. * @param withdrawSeasons The number of Seasons required to Withdraw a Deposit. * @param lastSopSeason The Season in which the most recent consecutive series of Seasons of Plenty ended. * @param rainStart Stores the most recent Season in which Rain started. * @param raining True if it is Raining (P > 1, Pod Rate Excessively Low). * @param fertilizing True if Beanstalk has Fertilizer left to be paid off. * @param sunriseBlock The block of the start of the current Season. * @param abovePeg Boolean indicating whether the previous Season was above or below peg. * @param stemStartSeason // season in which the stem storage method was introduced. * @param stemScaleSeason // season in which the stem v1.1 was introduced, where stems are not truncated anymore. * @param beanEthStartMintingSeason // Season to start minting in Bean:Eth pool after migrating liquidity out of the pool to protect against Pump failure. * This allows for greater precision of stems, and requires a soft migration (see {LibTokenSilo.removeDepositFromAccount}) * @param start The timestamp of the Beanstalk deployment rounded down to the nearest hour. * @param period The length of each season in Beanstalk in seconds. * @param timestamp The timestamp of the start of the current Season. */ struct Season { uint32 current; // ─────────────────┐ 4 uint32 lastSop; // │ 4 (8) uint8 withdrawSeasons; // │ 1 (9) uint32 lastSopSeason; // │ 4 (13) uint32 rainStart; // │ 4 (17) bool raining; // │ 1 (18) bool fertilizing; // │ 1 (19) uint32 sunriseBlock; // │ 4 (23) bool abovePeg; // | 1 (24) uint16 stemStartSeason; // | 2 (26) uint16 stemScaleSeason; // | 2 (28/32) uint32 beanEthStartMintingSeason; //┘ 4 (32/32) NOTE: Reset and delete after Bean:wStEth migration has been completed. uint256 start; uint256 period; uint256 timestamp; } /** * @notice System-level Weather state variables. * @param deprecated 2 slots that were previously used. * @param lastDSoil Delta Soil; the number of Soil purchased last Season. * @param lastSowTime The number of seconds it for Soil to sell out last Season. * @param thisSowTime The number of seconds it for Soil to sell out this Season. * @param t The Temperature; the maximum interest rate during the current Season for sowing Beans in Soil. Adjusted each Season. */ struct Weather { uint256[2] deprecated; uint128 lastDSoil; // ───┐ 16 (16) uint32 lastSowTime; // │ 4 (20) uint32 thisSowTime; // │ 4 (24) uint32 t; // ─────────────┘ 4 (28/32) } /** * @notice Describes a Fundraiser. * @param payee The address to be paid after the Fundraiser has been fully funded. * @param token The token address that used to raise funds for the Fundraiser. * @param total The total number of Tokens that need to be raised to complete the Fundraiser. * @param remaining The remaining number of Tokens that need to to complete the Fundraiser. * @param start The timestamp at which the Fundraiser started (Fundraisers cannot be started and funded in the same block). */ struct Fundraiser { address payee; address token; uint256 total; uint256 remaining; uint256 start; } /** * @notice Describes the settings for each Token that is Whitelisted in the Silo. * @param selector The encoded BDV function selector for the token that pertains to * an external view Beanstalk function with the following signature: * ``` * function tokenToBdv(uint256 amount) external view returns (uint256); * ``` * It is called by `LibTokenSilo` through the use of `delegatecall` * to calculate a token's BDV at the time of Deposit. * @param stalkEarnedPerSeason represents how much Stalk one BDV of the underlying deposited token * grows each season. In the past, this was represented by seeds. This is stored as 1e6, plus stalk is stored * as 1e10, so 1 legacy seed would be 1e6 * 1e10. * @param stalkIssuedPerBdv The Stalk Per BDV that the Silo grants in exchange for Depositing this Token. * previously called stalk. * @param milestoneSeason The last season in which the stalkEarnedPerSeason for this token was updated. * @param milestoneStem The cumulative amount of grown stalk per BDV for this token at the last stalkEarnedPerSeason update. * @param encodeType determine the encoding type of the selector. * a encodeType of 0x00 means the selector takes an input amount. * 0x01 means the selector takes an input amount and a token. * @param gpSelector The encoded gaugePoint function selector for the token that pertains to * an external view Beanstalk function with the following signature: * ``` * function gaugePoints( * uint256 currentGaugePoints, * uint256 optimalPercentDepositedBdv, * uint256 percentOfDepositedBdv * ) external view returns (uint256); * ``` * @param lwSelector The encoded liquidityWeight function selector for the token that pertains to * an external view Beanstalk function with the following signature `function liquidityWeight()` * @param optimalPercentDepositedBdv The target percentage of the total LP deposited BDV for this token. 6 decimal precision. * @param gaugePoints the amount of Gauge points this LP token has in the LP Gauge. Only used for LP whitelisted assets. * GaugePoints has 18 decimal point precision (1 Gauge point = 1e18). * @dev A Token is considered Whitelisted if there exists a non-zero {SiloSettings} selector. */ struct SiloSettings { bytes4 selector; // ────────────────────┐ 4 uint32 stalkEarnedPerSeason; // │ 4 (8) uint32 stalkIssuedPerBdv; // │ 4 (12) uint32 milestoneSeason; // │ 4 (16) int96 milestoneStem; // │ 12 (28) bytes1 encodeType; // │ 1 (29) int24 deltaStalkEarnedPerSeason; // ────┘ 3 (32) bytes4 gpSelector; // ────────────────┐ 4 bytes4 lwSelector; // │ 4 (8) uint128 gaugePoints; // │ 16 (24) uint64 optimalPercentDepositedBdv; // ──┘ 8 (32) } /** * @notice Describes the settings for each Unripe Token in Beanstalk. * @param underlyingToken The address of the Token underlying the Unripe Token. * @param balanceOfUnderlying The number of Tokens underlying the Unripe Tokens (redemption pool). * @param merkleRoot The Merkle Root used to validate a claim of Unripe Tokens. * @dev An Unripe Token is a vesting Token that is redeemable for a a pro rata share * of the `balanceOfUnderlying`, subject to a penalty based on the percent of * Unfertilized Beans paid back. * * There were two Unripe Tokens added at Replant: * - Unripe Bean, with its `underlyingToken` as BEAN; * - Unripe LP, with its `underlyingToken` as BEAN:3CRV LP. * * Unripe Tokens are initially distributed through the use of a `merkleRoot`. * * The existence of a non-zero {UnripeSettings} implies that a Token is an Unripe Token. */ struct UnripeSettings { address underlyingToken; uint256 balanceOfUnderlying; bytes32 merkleRoot; } /** * @notice System level variables used in the seed Gauge System. * @param averageGrownStalkPerBdvPerSeason The average Grown Stalk Per BDV * that beanstalk issues each season. * @param beanToMaxLpGpPerBdvRatio a scalar of the gauge points(GP) per bdv * issued to the largest LP share and Bean. 6 decimal precision. * @dev a beanToMaxLpGpPerBdvRatio of 0 means LP should be incentivized the most, * and that beans will have the minimum seeds ratio. see {LibGauge.getBeanToMaxLpGpPerBdvRatioScaled} */ struct SeedGauge { uint128 averageGrownStalkPerBdvPerSeason; uint128 beanToMaxLpGpPerBdvRatio; } /** * @notice Stores the twaReserves for each well during the sunrise function. */ struct TwaReserves { uint128 reserve0; uint128 reserve1; } /** * @notice Stores the total germination amounts for each whitelisted token. */ struct Deposited { uint128 amount; uint128 bdv; } /** * @notice Stores the system level germination data. */ struct TotalGerminating { mapping(address => Deposited) deposited; } struct Sr { uint128 stalk; uint128 roots; } } /** * @title AppStorage * @author Publius * @notice Defines the state object for Beanstalk. * @param deprecated_index DEPRECATED: Was the index of the BEAN token in the BEAN:ETH Uniswap V2 pool. * @param deprecated_cases DEPRECATED: The 24 Weather cases used in cases V1 (array has 32 items, but caseId = 3 (mod 4) are not cases) * @param paused True if Beanstalk is Paused. * @param pausedAt The timestamp at which Beanstalk was last paused. * @param season Storage.Season * @param c Storage.Contracts * @param f Storage.Field * @param g Storage.Governance * @param co Storage.CurveMetapoolOracle * @param r Storage.Rain * @param s Storage.Silo * @param reentrantStatus An intra-transaction state variable to protect against reentrance. * @param w Storage.Weather * @param earnedBeans The number of Beans distributed to the Silo that have not yet been Deposited as a result of the Earn function being called. * @param deprecated DEPRECATED - 14 slots that used to store state variables which have been deprecated through various updates. Storage slots can be left alone or reused. * @param a mapping (address => Account.State) * @param deprecated_bip0Start DEPRECATED - bip0Start was used to aid in a migration that occured alongside BIP-0. * @param deprecated_hotFix3Start DEPRECATED - hotFix3Start was used to aid in a migration that occured alongside HOTFIX-3. * @param fundraisers A mapping from Fundraiser ID to Storage.Fundraiser. * @param fundraiserIndex The number of Fundraisers that have occured. * @param deprecated_isBudget DEPRECATED - Budget Facet was removed in BIP-14. * @param podListings A mapping from Plot Index to the hash of the Pod Listing. * @param podOrders A mapping from the hash of a Pod Order to the amount of Pods that the Pod Order is still willing to buy. * @param siloBalances A mapping from Token address to Silo Balance storage (amount deposited and withdrawn). * @param ss A mapping from Token address to Silo Settings for each Whitelisted Token. If a non-zero storage exists, a Token is whitelisted. * @param deprecated2 DEPRECATED - 2 slots that used to store state variables which have been deprecated through various updates. Storage slots can be left alone or reused. * @param deprecated_newEarnedStalk the amount of earned stalk issued this season. Since 1 stalk = 1 bean, it represents the earned beans as well. * @param sops A mapping from Season to Plenty Per Root (PPR) in that Season. Plenty Per Root is 0 if a Season of Plenty did not occur. * @param internalTokenBalance A mapping from Farmer address to Token address to Internal Balance. It stores the amount of the Token that the Farmer has stored as an Internal Balance in Beanstalk. * @param unripeClaimed True if a Farmer has Claimed an Unripe Token. A mapping from Farmer to Unripe Token to its Claim status. * @param u Unripe Settings for a given Token address. The existence of a non-zero Unripe Settings implies that the token is an Unripe Token. The mapping is from Token address to Unripe Settings. * @param fertilizer A mapping from Fertilizer Id to the supply of Fertilizer for each Id. * @param nextFid A linked list of Fertilizer Ids ordered by Id number. Fertilizer Id is the Beans Per Fertilzer level at which the Fertilizer no longer receives Beans. Sort in order by which Fertilizer Id expires next. * @param activeFertilizer The number of active Fertilizer. * @param fertilizedIndex The total number of Fertilizer Beans. * @param unfertilizedIndex The total number of Unfertilized Beans ever. * @param fFirst The lowest active Fertilizer Id (start of linked list that is stored by nextFid). * @param fLast The highest active Fertilizer Id (end of linked list that is stored by nextFid). * @param bpf The cumulative Beans Per Fertilizer (bfp) minted over all Season. * @param deprecated_vestingPeriodRoots deprecated - removed in BIP-39 in favor of germination. * @param recapitalized The number of USDC that has been recapitalized in the Barn Raise. * @param isFarm Stores whether the function is wrapped in the `farm` function (1 if not, 2 if it is). * @param ownerCandidate Stores a candidate address to transfer ownership to. The owner must claim the ownership transfer. * @param wellOracleSnapshots A mapping from Well Oracle address to the Well Oracle Snapshot. * @param deprecated_beanEthPrice DEPRECATED - The price of bean:eth, originally used to calculate the incentive reward. Deprecated in favor of calculating using twaReserves. * @param twaReserves A mapping from well to its twaReserves. Stores twaReserves during the sunrise function. Returns 1 otherwise for each asset. Currently supports 2 token wells. * @param migratedBdvs Stores the total migrated BDV since the implementation of the migrated BDV counter. See {LibLegacyTokenSilo.incrementMigratedBdv} for more info. * @param usdEthPrice Stores the usdEthPrice during the sunrise() function. Returns 1 otherwise. * @param seedGauge Stores the seedGauge. * @param casesV2 Stores the 144 Weather and seedGauge cases. * @param oddGerminating Stores germinating data during odd seasons. * @param evenGerminating Stores germinating data during even seasons. * @param whitelistedStatues Stores a list of Whitelist Statues for all tokens that have been Whitelisted and have not had their Whitelist Status manually removed. * @param sopWell Stores the well that will be used upon a SOP. Unintialized until a SOP occurs, and is kept constant afterwards. * @param barnRaiseWell Stores the well that the Barn Raise adds liquidity to. */ struct AppStorage { uint8 deprecated_index; int8[32] deprecated_cases; bool paused; // ────────┐ 1 uint128 pausedAt; // ───┘ 16 (17/32) Storage.Season season; Storage.Contracts c; Storage.Field f; Storage.Governance g; Storage.CurveMetapoolOracle co; Storage.Rain r; Storage.Silo s; uint256 reentrantStatus; Storage.Weather w; uint256 earnedBeans; uint256[14] deprecated; mapping (address => Account.State) a; uint32 deprecated_bip0Start; // ─────┐ 4 uint32 deprecated_hotFix3Start; // ──┘ 4 (8/32) mapping (uint32 => Storage.Fundraiser) fundraisers; uint32 fundraiserIndex; // 4 (4/32) mapping (address => bool) deprecated_isBudget; mapping(uint256 => bytes32) podListings; mapping(bytes32 => uint256) podOrders; mapping(address => Storage.AssetSilo) siloBalances; mapping(address => Storage.SiloSettings) ss; uint256[2] deprecated2; uint128 deprecated_newEarnedStalk; // ──────┐ 16 uint128 deprecated_vestingPeriodRoots; // ──┘ 16 (32/32) mapping (uint32 => uint256) sops; // Internal Balances mapping(address => mapping(IERC20 => uint256)) internalTokenBalance; // Unripe mapping(address => mapping(address => bool)) unripeClaimed; mapping(address => Storage.UnripeSettings) u; // Fertilizer mapping(uint128 => uint256) fertilizer; mapping(uint128 => uint128) nextFid; uint256 activeFertilizer; uint256 fertilizedIndex; uint256 unfertilizedIndex; uint128 fFirst; uint128 fLast; uint128 bpf; uint256 recapitalized; // Farm uint256 isFarm; // Ownership address ownerCandidate; // Well mapping(address => bytes) wellOracleSnapshots; uint256 deprecated_beanEthPrice; // Silo V3 BDV Migration mapping(address => uint256) migratedBdvs; // Well/Curve + USD Price Oracle mapping(address => Storage.TwaReserves) twaReserves; mapping(address => uint256) usdTokenPrice; // Seed Gauge Storage.SeedGauge seedGauge; bytes32[144] casesV2; // Germination Storage.TotalGerminating oddGerminating; Storage.TotalGerminating evenGerminating; // mapping from season => unclaimed germinating stalk and roots mapping(uint32 => Storage.Sr) unclaimedGerminating; Storage.WhitelistStatus[] whitelistStatuses; address sopWell; }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import "./AppStorage.sol"; /** * @author Beanstalk Farms * @title Variation of Oepn Zeppelins reentrant guard to include Silo Update * https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts%2Fsecurity%2FReentrancyGuard.sol **/ abstract contract ReentrancyGuard { uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; AppStorage internal s; modifier nonReentrant() { require(s.reentrantStatus != _ENTERED, "ReentrancyGuard: reentrant call"); s.reentrantStatus = _ENTERED; _; s.reentrantStatus = _NOT_ENTERED; } }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {C} from "contracts/C.sol"; import {ReentrancyGuard} from "contracts/beanstalk/ReentrancyGuard.sol"; import {LibWellMinting} from "contracts/libraries/Minting/LibWellMinting.sol"; import {SignedSafeMath} from "@openzeppelin/contracts/math/SignedSafeMath.sol"; import {LibWhitelistedTokens} from "contracts/libraries/Silo/LibWhitelistedTokens.sol"; /** * @title Oracle * @author Publius, Chaikitty, Brean * @notice Tracks the Delta B in available pools. */ contract Oracle is ReentrancyGuard { using SignedSafeMath for int256; //////////////////// ORACLE INTERNAL //////////////////// function stepOracle() internal returns (int256 deltaB) { address[] memory tokens = LibWhitelistedTokens.getWhitelistedWellLpTokens(); for (uint256 i = 0; i < tokens.length; i++) { deltaB = deltaB.add(LibWellMinting.capture(tokens[i])); } s.season.timestamp = block.timestamp; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.6; pragma experimental ABIEncoderV2; import {SafeCast} from "@openzeppelin/contracts/utils/SafeCast.sol"; import {LibFertilizer, SafeMath} from "contracts/libraries/LibFertilizer.sol"; import {LibSafeMath128} from "contracts/libraries/LibSafeMath128.sol"; import {Oracle, C} from "./Oracle.sol"; /** * @title Sun * @author Publius * @notice Sun controls the minting of new Beans to Fertilizer, the Field, and the Silo. */ contract Sun is Oracle { using SafeCast for uint256; using SafeMath for uint256; using LibSafeMath128 for uint128; /// @dev When Fertilizer is Active, it receives 1/3 of new Bean mints. uint256 private constant FERTILIZER_DENOMINATOR = 3; /// @dev After Fertilizer, Harvestable Pods receive 1/2 of new Bean mints. uint256 private constant HARVEST_DENOMINATOR = 2; /// @dev When the Pod Rate is high, issue less Soil. uint256 private constant SOIL_COEFFICIENT_HIGH = 0.5e18; /// @dev When the Pod Rate is low, issue more Soil. uint256 private constant SOIL_COEFFICIENT_LOW = 1.5e18; /** * @notice Emitted during Sunrise when Beans are distributed to the Field, the Silo, and Fertilizer. * @param season The Season in which Beans were distributed. * @param toField The number of Beans distributed to the Field. * @param toSilo The number of Beans distributed to the Silo. * @param toFertilizer The number of Beans distributed to Fertilizer. */ event Reward( uint32 indexed season, uint256 toField, uint256 toSilo, uint256 toFertilizer ); /** * @notice Emitted during Sunrise when Beanstalk adjusts the amount of available Soil. * @param season The Season in which Soil was adjusted. * @param soil The new amount of Soil available. */ event Soil( uint32 indexed season, uint256 soil ); //////////////////// SUN INTERNAL //////////////////// /** * @param deltaB Pre-calculated deltaB from {Oracle.stepOracle}. * @param caseId Pre-calculated Weather case from {Weather.calcCaseId}. */ function stepSun(int256 deltaB, uint256 caseId) internal { // Above peg if (deltaB > 0) { uint256 newHarvestable = rewardBeans(uint256(deltaB)); setSoilAbovePeg(newHarvestable, caseId); s.season.abovePeg = true; } // Below peg else { setSoil(uint256(-deltaB)); s.season.abovePeg = false; } } //////////////////// REWARD BEANS //////////////////// /** * @dev Mints and distributes Beans to Fertilizer, the Field, and the Silo. */ function rewardBeans(uint256 newSupply) internal returns (uint256 newHarvestable) { uint256 newFertilized; C.bean().mint(address(this), newSupply); // Distribute first to Fertilizer if some Fertilizer are active if (s.season.fertilizing) { newFertilized = rewardToFertilizer(newSupply); newSupply = newSupply.sub(newFertilized); } // Distribute next to the Field if some Pods are still outstanding if (s.f.harvestable < s.f.pods) { newHarvestable = rewardToHarvestable(newSupply); newSupply = newSupply.sub(newHarvestable); } // Distribute remainder to the Silo rewardToSilo(newSupply); emit Reward(s.season.current, newHarvestable, newSupply, newFertilized); } /** * @dev Distributes Beans to Fertilizer. */ function rewardToFertilizer(uint256 amount) internal returns (uint256 newFertilized) { // 1/3 of new Beans being minted uint256 maxNewFertilized = amount.div(FERTILIZER_DENOMINATOR); // Get the new Beans per Fertilizer and the total new Beans per Fertilizer uint256 newBpf = maxNewFertilized.div(s.activeFertilizer); uint256 oldTotalBpf = s.bpf; uint256 newTotalBpf = oldTotalBpf.add(newBpf); // Get the end Beans per Fertilizer of the first Fertilizer to run out. uint256 firstEndBpf = s.fFirst; // If the next fertilizer is going to run out, then step BPF according while(newTotalBpf >= firstEndBpf) { // Calculate BPF and new Fertilized when the next Fertilizer ID ends newBpf = firstEndBpf.sub(oldTotalBpf); newFertilized = newFertilized.add(newBpf.mul(s.activeFertilizer)); // If there is no more fertilizer, end if (!LibFertilizer.pop()) { s.bpf = uint128(firstEndBpf); // SafeCast unnecessary here. s.fertilizedIndex = s.fertilizedIndex.add(newFertilized); require(s.fertilizedIndex == s.unfertilizedIndex, "Paid != owed"); return newFertilized; } // Calculate new Beans per Fertilizer values newBpf = maxNewFertilized.sub(newFertilized).div(s.activeFertilizer); oldTotalBpf = firstEndBpf; newTotalBpf = oldTotalBpf.add(newBpf); firstEndBpf = s.fFirst; } // Distribute the rest of the Fertilized Beans s.bpf = uint128(newTotalBpf); // SafeCast unnecessary here. newFertilized = newFertilized.add(newBpf.mul(s.activeFertilizer)); s.fertilizedIndex = s.fertilizedIndex.add(newFertilized); } /** * @dev Distributes Beans to the Field. The next `amount` Pods in the Pod Line * become Harvestable. */ function rewardToHarvestable(uint256 amount) internal returns (uint256 newHarvestable) { uint256 notHarvestable = s.f.pods - s.f.harvestable; // Note: SafeMath is redundant here. newHarvestable = amount.div(HARVEST_DENOMINATOR); newHarvestable = newHarvestable > notHarvestable ? notHarvestable : newHarvestable; s.f.harvestable = s.f.harvestable.add(newHarvestable); } /** * @dev Distribute Beans to the Silo. Stalk & Earned Beans are created here; * Farmers can claim them through {SiloFacet.plant}. */ function rewardToSilo(uint256 amount) internal { // NOTE that the Beans have already been minted (see {rewardBeans}). // // `s.earnedBeans` is an accounting mechanism that tracks the total number // of Earned Beans that are claimable by Stalkholders. When claimed via `plant()`, // it is decremented. See {Silo.sol:_plant} for more details. // SafeCast not necessary as `seasonStalk.toUint128();` will fail if amount > type(uint128).max. s.earnedBeans = s.earnedBeans.add(amount.toUint128()); // Mint Stalk (as Earned Stalk). Farmers can claim their Earned Stalk via {SiloFacet.sol:plant}. // // Stalk is created here, rather than in {rewardBeans}, because only // Beans that are allocated to the Silo will receive Stalk. // Constant is used here rather than s.ss[BEAN].stalkIssuedPerBdv // for gas savings. s.s.stalk = s.s.stalk.add(amount.mul(C.STALK_PER_BEAN)); // removed at ebip-13. Will be replaced upon seed gauge BIP. // s.newEarnedStalk = seasonStalk.toUint128(); // s.vestingPeriodRoots = 0; s.siloBalances[C.BEAN].deposited = s .siloBalances[C.BEAN] .deposited .add(amount.toUint128()); // SafeCast not necessary as the block above will fail if amount > type(uint128).max. s.siloBalances[C.BEAN].depositedBdv = s .siloBalances[C.BEAN] .depositedBdv .add(uint128(amount)); } //////////////////// SET SOIL //////////////////// /** * @param newHarvestable The number of Beans that were minted to the Field. * @param caseId The current Weather Case. * @dev When above peg, Beanstalk wants to gauge demand for Soil. Here it * issues the amount of Soil that would result in the same number of Pods * as became Harvestable during the last Season. * * When the Pod Rate is high, Beanstalk issues less Soil. * When the Pod Rate is low, Beanstalk issues more Soil. */ function setSoilAbovePeg(uint256 newHarvestable, uint256 caseId) internal { uint256 newSoil = newHarvestable.mul(100).div(100 + s.w.t); if (caseId.mod(36) >= 24) { newSoil = newSoil.mul(SOIL_COEFFICIENT_HIGH).div(C.PRECISION); // high podrate } else if (caseId.mod(36) < 8) { newSoil = newSoil.mul(SOIL_COEFFICIENT_LOW).div(C.PRECISION); // low podrate } setSoil(newSoil); } function setSoil(uint256 amount) internal { s.f.soil = amount.toUint128(); emit Soil(s.season.current, amount.toUint128()); } }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {LibEvaluate} from "contracts/libraries/LibEvaluate.sol"; import {LibSafeMath128} from "contracts/libraries/LibSafeMath128.sol"; import {LibCases} from "contracts/libraries/LibCases.sol"; import {Sun, SafeMath, C} from "./Sun.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IBeanstalkWellFunction} from "contracts/interfaces/basin/IBeanstalkWellFunction.sol"; import {LibWell} from "contracts/libraries/Well/LibWell.sol"; import {IWell, Call} from "contracts/interfaces/basin/IWell.sol"; import {IInstantaneousPump} from "contracts/interfaces/basin/pumps/IInstantaneousPump.sol"; import {SignedSafeMath} from "@openzeppelin/contracts/math/SignedSafeMath.sol"; /** * @title Weather * @author Publius * @notice Weather controls the Temperature and Grown Stalk to LP on the Farm. */ contract Weather is Sun { using SafeMath for uint256; using SignedSafeMath for int256; using LibSafeMath128 for uint128; uint128 internal constant MAX_BEAN_LP_GP_PER_BDV_RATIO = 100e18; /** * @notice Emitted when the Temperature (fka "Weather") changes. * @param season The current Season * @param caseId The Weather case, which determines how much the Temperature is adjusted. * @param absChange The absolute change in Temperature. * @dev formula: T_n = T_n-1 +/- bT */ event TemperatureChange(uint256 indexed season, uint256 caseId, int8 absChange); /** * @notice Emitted when the grownStalkToLP changes. * @param season The current Season * @param caseId The Weather case, which determines how the BeanToMaxLPGpPerBDVRatio is adjusted. * @param absChange The absolute change in the BeanToMaxLPGpPerBDVRatio. * @dev formula: L_n = L_n-1 +/- bL */ event BeanToMaxLpGpPerBdvRatioChange(uint256 indexed season, uint256 caseId, int80 absChange); /** * @notice Emitted when Beans are minted during the Season of Plenty. * @param season The Season in which Beans were minted for distribution. * @param well The Well that the SOP occurred in. * @param token The token that was swapped for Beans. * @param amount The amount of 3CRV which was received for swapping Beans. * @param toField The amount of Beans which were distributed to remaining Pods in the Field. */ event SeasonOfPlenty(uint256 indexed season, address well, address token, uint256 amount, uint256 toField); //////////////////// WEATHER INTERNAL //////////////////// /** * @notice from deltaB, podRate, change in soil demand, and liquidity to supply ratio, * calculate the caseId, and update the temperature and grownStalkPerBdvToLp. * @param deltaB Pre-calculated deltaB from {Oracle.stepOracle}. * @dev A detailed explanation of the temperature and grownStalkPerBdvToLp * mechanism can be found in the Beanstalk whitepaper. * An explanation of state variables can be found in {AppStorage}. */ function calcCaseIdandUpdate(int256 deltaB) internal returns (uint256) { uint256 beanSupply = C.bean().totalSupply(); // prevents infinite L2SR and podrate if (beanSupply == 0) { s.w.t = 1; return 9; // Reasonably low } // Calculate Case Id ( uint256 caseId, address sopWell, bool oracleFailure ) = LibEvaluate.evaluateBeanstalk(deltaB, beanSupply); updateTemperatureAndBeanToMaxLpGpPerBdvRatio(caseId, oracleFailure); handleRain(caseId, sopWell); return caseId; } /** * @notice updates the temperature and BeanToMaxLpGpPerBdvRatio, based on the caseId. * @param caseId the state beanstalk is in, based on the current season. * @dev currently, an oracle failure does not affect the temperature, as * the temperature is not affected by liquidity levels. The function will * need to be updated if the temperature is affected by liquidity levels. * This is implemented such that liveliness in change in temperature is retained. */ function updateTemperatureAndBeanToMaxLpGpPerBdvRatio(uint256 caseId, bool oracleFailure) internal { LibCases.CaseData memory cd = LibCases.decodeCaseData(caseId); updateTemperature(cd.bT, caseId); // if one of the oracles needed to calculate usd liquidity fails, // the beanToMaxLpGpPerBdvRatio should not be updated. if(oracleFailure) return; updateBeanToMaxLPRatio(cd.bL, caseId); } /** * @notice Changes the current Temperature `s.w.t` based on the Case Id. * @dev bT are set during edge cases such that the event emitted is valid. */ function updateTemperature(int8 bT, uint256 caseId) private { uint256 t = s.w.t; if (bT < 0) { if (t <= uint256(-bT)) { // if (change < 0 && t <= uint32(-change)), // then 0 <= t <= type(int8).max because change is an int8. // Thus, downcasting t to an int8 will not cause overflow. bT = 1 - int8(t); s.w.t = 1; } else { s.w.t = uint32(t - uint256(-bT)); } } else { s.w.t = uint32(t + uint256(bT)); } emit TemperatureChange(s.season.current, caseId, bT); } /** * @notice Changes the grownStalkPerBDVPerSeason based on the CaseId. * @dev bL are set during edge cases such that the event emitted is valid. */ function updateBeanToMaxLPRatio(int80 bL, uint256 caseId) private { uint128 beanToMaxLpGpPerBdvRatio = s.seedGauge.beanToMaxLpGpPerBdvRatio; if (bL < 0) { if (beanToMaxLpGpPerBdvRatio <= uint128(-bL)) { bL = -int80(beanToMaxLpGpPerBdvRatio); s.seedGauge.beanToMaxLpGpPerBdvRatio = 0; } else { s.seedGauge.beanToMaxLpGpPerBdvRatio = beanToMaxLpGpPerBdvRatio.sub(uint128(-bL)); } } else { if (beanToMaxLpGpPerBdvRatio.add(uint128(bL)) >= MAX_BEAN_LP_GP_PER_BDV_RATIO) { // if (change > 0 && 100e18 - beanToMaxLpGpPerBdvRatio <= bL), // then bL cannot overflow. bL = int80(MAX_BEAN_LP_GP_PER_BDV_RATIO.sub(beanToMaxLpGpPerBdvRatio)); s.seedGauge.beanToMaxLpGpPerBdvRatio = MAX_BEAN_LP_GP_PER_BDV_RATIO; } else { s.seedGauge.beanToMaxLpGpPerBdvRatio = beanToMaxLpGpPerBdvRatio.add(uint128(bL)); } } emit BeanToMaxLpGpPerBdvRatioChange(s.season.current, caseId, bL); } /** * @dev Oversaturated was previously referred to as Raining and thus code * references mentioning Rain really refer to Oversaturation. If P > 1 and the * Pod Rate is less than 5%, the Farm is Oversaturated. If it is Oversaturated * for a Season, each Season in which it continues to be Oversaturated, it Floods. */ function handleRain(uint256 caseId, address well) internal { // cases % 36 3-8 represent the case where the pod rate is less than 5% and P > 1. if (caseId.mod(36) < 3 || caseId.mod(36) > 8) { if (s.season.raining) { s.season.raining = false; } return; } else if (!s.season.raining) { s.season.raining = true; // Set the plenty per root equal to previous rain start. s.sops[s.season.current] = s.sops[s.season.rainStart]; s.season.rainStart = s.season.current; s.r.pods = s.f.pods; s.r.roots = s.s.roots; } else { if (s.r.roots > 0) { // initialize sopWell if it is not already set. if (s.sopWell == address(0)) s.sopWell = well; sop(); } } } /** * @dev Flood was previously called a "Season of Plenty" (SOP for short). * When Beanstalk has been Oversaturated for a Season, Beanstalk returns the * Bean price to its peg by minting additional Beans and selling them directly * on the sop well. Proceeds from the sale in the form of WETH are distributed to * Stalkholders at the beginning of a Season in proportion to their Stalk * ownership when the Farm became Oversaturated. Also, at the beginning of the * Flood, all Pods that were minted before the Farm became Oversaturated Ripen * and become Harvestable. * For more information On Oversaturation see {Weather.handleRain}. */ function sop() private { // calculate the beans from a sop. // sop beans uses the min of the current and instantaneous reserves of the sop well, // rather than the twaReserves in order to get bean back to peg. address sopWell = s.sopWell; (uint256 newBeans, IERC20 sopToken) = calculateSop(sopWell); if (newBeans == 0) return; uint256 sopBeans = uint256(newBeans); uint256 newHarvestable; // Pay off remaining Pods if any exist. if (s.f.harvestable < s.r.pods) { newHarvestable = s.r.pods - s.f.harvestable; s.f.harvestable = s.f.harvestable.add(newHarvestable); C.bean().mint(address(this), newHarvestable.add(sopBeans)); } else { C.bean().mint(address(this), sopBeans); } // Approve and Swap Beans for the non-bean token of the SOP well. C.bean().approve(sopWell, sopBeans); uint256 amountOut = IWell(sopWell).swapFrom( C.bean(), sopToken, sopBeans, 0, address(this), type(uint256).max ); rewardSop(amountOut); emit SeasonOfPlenty(s.season.current, sopWell, address(sopToken), amountOut, newHarvestable); } /** * @dev Allocate `sop token` during a Season of Plenty. */ function rewardSop(uint256 amount) private { s.sops[s.season.rainStart] = s.sops[s.season.lastSop].add( amount.mul(C.SOP_PRECISION).div(s.r.roots) ); s.season.lastSop = s.season.rainStart; s.season.lastSopSeason = s.season.current; } /** * Calculates the amount of beans that should be minted in a sop. * @dev the instanteous EMA reserves are used rather than the twa reserves * as the twa reserves are not indiciative of the current deltaB in the pool. * * Generalized for a single well. Sop does not support multiple wells. */ function calculateSop(address well) private view returns (uint256 sopBeans, IERC20 sopToken){ // if the sopWell was not initialized, the should not occur. if (well == address(0)) return (0, IERC20(0)); IWell sopWell = IWell(well); IERC20[] memory tokens = sopWell.tokens(); Call[] memory pumps = sopWell.pumps(); IInstantaneousPump pump = IInstantaneousPump(pumps[0].target); uint256[] memory instantaneousReserves = pump.readInstantaneousReserves(well, pumps[0].data); uint256[] memory currentReserves = sopWell.getReserves(); Call memory wellFunction = sopWell.wellFunction(); ( uint256[] memory ratios, uint256 beanIndex, bool success ) = LibWell.getRatiosAndBeanIndex(tokens); // If the USD Oracle oracle call fails, the sop should not occur. // return 0 rather than revert to prevent sunrise from failing. if (!success) return (0, IERC20(0)); // compare the beans at peg using the instantaneous reserves, // and the current reserves. uint256 instantaneousBeansAtPeg = IBeanstalkWellFunction(wellFunction.target) .calcReserveAtRatioSwap( instantaneousReserves, beanIndex, ratios, wellFunction.data ); uint256 currentBeansAtPeg = IBeanstalkWellFunction(wellFunction.target) .calcReserveAtRatioSwap( currentReserves, beanIndex, ratios, wellFunction.data ); // Calculate the signed Sop beans for the two reserves. int256 lowestSopBeans = int256(instantaneousBeansAtPeg).sub(int256(instantaneousReserves[beanIndex])); int256 currentSopBeans = int256(currentBeansAtPeg).sub(int256(currentReserves[beanIndex])); // Use the minimum of the two. if (lowestSopBeans > currentSopBeans) { lowestSopBeans = currentSopBeans; } // If the sopBeans is negative, the sop should not occur. if (lowestSopBeans < 0) return (0, IERC20(0)); // SafeCast not necessary due to above check. sopBeans = uint256(lowestSopBeans); // the sopToken is the non bean token in the well. sopToken = tokens[beanIndex == 0 ? 1 : 0]; } }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import "./interfaces/IBean.sol"; import "./interfaces/ICurve.sol"; import "./interfaces/IFertilizer.sol"; import "./interfaces/IProxyAdmin.sol"; import "./libraries/Decimal.sol"; /** * @title C * @author Publius * @notice Contains constants used throughout Beanstalk. */ library C { using Decimal for Decimal.D256; using SafeMath for uint256; //////////////////// Globals //////////////////// uint256 internal constant PRECISION = 1e18; uint256 private constant CHAIN_ID = 1; bytes constant BYTES_ZERO = new bytes(0); /// @dev The block time for the chain in seconds. uint256 internal constant BLOCK_LENGTH_SECONDS = 12; //////////////////// Season //////////////////// /// @dev The length of a Season meaured in seconds. uint256 private constant CURRENT_SEASON_PERIOD = 3600; // 1 hour uint256 internal constant SOP_PRECISION = 1e24; //////////////////// Silo //////////////////// uint256 internal constant SEEDS_PER_BEAN = 2; uint256 internal constant STALK_PER_BEAN = 10000; uint256 private constant ROOTS_BASE = 1e12; //////////////////// Exploit Migration //////////////////// uint256 private constant UNRIPE_LP_PER_DOLLAR = 1884592; // 145_113_507_403_282 / 77_000_000 uint256 private constant ADD_LP_RATIO = 866616; uint256 private constant INITIAL_HAIRCUT = 185564685220298701; //////////////////// Contracts //////////////////// address internal constant BEAN = 0xBEA0000029AD1c77D3d5D23Ba2D8893dB9d1Efab; address internal constant CURVE_BEAN_METAPOOL = 0xc9C32cd16Bf7eFB85Ff14e0c8603cc90F6F2eE49; address internal constant UNRIPE_BEAN = 0x1BEA0050E63e05FBb5D8BA2f10cf5800B6224449; address internal constant UNRIPE_LP = 0x1BEA3CcD22F4EBd3d37d731BA31Eeca95713716D; address private constant CURVE_3_POOL = 0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7; address private constant THREE_CRV = 0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490; address private constant FERTILIZER = 0x402c84De2Ce49aF88f5e2eF3710ff89bFED36cB6; address private constant FERTILIZER_ADMIN = 0xfECB01359263C12Aa9eD838F878A596F0064aa6e; address private constant TRI_CRYPTO = 0xc4AD29ba4B3c580e6D59105FFf484999997675Ff; address private constant TRI_CRYPTO_POOL = 0xD51a44d3FaE010294C616388b506AcdA1bfAAE46; address private constant CURVE_ZAP = 0xA79828DF1850E8a3A3064576f380D90aECDD3359; address private constant UNRIPE_CURVE_BEAN_LUSD_POOL = 0xD652c40fBb3f06d6B58Cb9aa9CFF063eE63d465D; address private constant UNRIPE_CURVE_BEAN_METAPOOL = 0x3a70DfA7d2262988064A2D051dd47521E43c9BdD; address internal constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; address internal constant USDT = 0xdAC17F958D2ee523a2206206994597C13D831ec7; address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; address internal constant WSTETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; // Use external contract for block.basefee as to avoid upgrading existing contracts to solidity v8 address private constant BASE_FEE_CONTRACT = 0x84292919cB64b590C0131550483707E43Ef223aC; //////////////////// Well //////////////////// uint256 internal constant WELL_MINIMUM_BEAN_BALANCE = 1000_000_000; // 1,000 Beans address internal constant BEAN_ETH_WELL = 0xBEA0e11282e2bB5893bEcE110cF199501e872bAd; address internal constant BEAN_WSTETH_WELL = 0xBeA0000113B0d182f4064C86B71c315389E4715D; // The index of the Bean and Weth token addresses in all BEAN/ETH Wells. uint256 internal constant BEAN_INDEX = 0; uint256 internal constant ETH_INDEX = 1; function getSeasonPeriod() internal pure returns (uint256) { return CURRENT_SEASON_PERIOD; } function getBlockLengthSeconds() internal pure returns (uint256) { return BLOCK_LENGTH_SECONDS; } function getChainId() internal pure returns (uint256) { return CHAIN_ID; } function getSeedsPerBean() internal pure returns (uint256) { return SEEDS_PER_BEAN; } function getStalkPerBean() internal pure returns (uint256) { return STALK_PER_BEAN; } function getRootsBase() internal pure returns (uint256) { return ROOTS_BASE; } /** * @dev The pre-exploit BEAN:3CRV Curve metapool address. */ function unripeLPPool1() internal pure returns (address) { return UNRIPE_CURVE_BEAN_METAPOOL; } /** * @dev The pre-exploit BEAN:LUSD Curve plain pool address. */ function unripeLPPool2() internal pure returns (address) { return UNRIPE_CURVE_BEAN_LUSD_POOL; } function unripeBean() internal pure returns (IERC20) { return IERC20(UNRIPE_BEAN); } function unripeLP() internal pure returns (IERC20) { return IERC20(UNRIPE_LP); } function bean() internal pure returns (IBean) { return IBean(BEAN); } function usdc() internal pure returns (IERC20) { return IERC20(USDC); } function curveMetapool() internal pure returns (ICurvePool) { return ICurvePool(CURVE_BEAN_METAPOOL); } function curve3Pool() internal pure returns (I3Curve) { return I3Curve(CURVE_3_POOL); } function curveZap() internal pure returns (ICurveZap) { return ICurveZap(CURVE_ZAP); } function curveZapAddress() internal pure returns (address) { return CURVE_ZAP; } function curve3PoolAddress() internal pure returns (address) { return CURVE_3_POOL; } function threeCrv() internal pure returns (IERC20) { return IERC20(THREE_CRV); } function fertilizer() internal pure returns (IFertilizer) { return IFertilizer(FERTILIZER); } function fertilizerAddress() internal pure returns (address) { return FERTILIZER; } function fertilizerAdmin() internal pure returns (IProxyAdmin) { return IProxyAdmin(FERTILIZER_ADMIN); } function triCryptoPoolAddress() internal pure returns (address) { return TRI_CRYPTO_POOL; } function triCrypto() internal pure returns (IERC20) { return IERC20(TRI_CRYPTO); } function unripeLPPerDollar() internal pure returns (uint256) { return UNRIPE_LP_PER_DOLLAR; } function dollarPerUnripeLP() internal pure returns (uint256) { return 1e12/UNRIPE_LP_PER_DOLLAR; } function exploitAddLPRatio() internal pure returns (uint256) { return ADD_LP_RATIO; } function precision() internal pure returns (uint256) { return PRECISION; } function initialRecap() internal pure returns (uint256) { return INITIAL_HAIRCUT; } }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {IWellFunction} from "./IWellFunction.sol"; /** * @title IBeanstalkWellFunction * @notice Defines all necessary functions for Beanstalk to support a Well Function in addition to functions defined in the primary interface. * This includes 2 functions to solve for a given reserve value suc that the average price between * the given reserve and all other reserves equals the average of the input ratios. * `calcReserveAtRatioSwap` assumes the target ratios are reached through executing a swap. * `calcReserveAtRatioLiquidity` assumes the target ratios are reached through adding/removing liquidity. */ interface IBeanstalkWellFunction is IWellFunction { /** * @notice Calculates the `j` reserve such that `π_{i | i != j} (d reserves_j / d reserves_i) = π_{i | i != j}(ratios_j / ratios_i)`. * assumes that reserve_j is being swapped for other reserves in the Well. * @dev used by Beanstalk to calculate the deltaB every Season * @param reserves The reserves of the Well * @param j The index of the reserve to solve for * @param ratios The ratios of reserves to solve for * @param data Well function data provided on every call * @return reserve The resulting reserve at the jth index */ function calcReserveAtRatioSwap( uint[] calldata reserves, uint j, uint[] calldata ratios, bytes calldata data ) external view returns (uint reserve); /** * @notice Calculates the `j` reserve such that `π_{i | i != j} (d reserves_j / d reserves_i) = π_{i | i != j}(ratios_j / ratios_i)`. * assumes that reserve_j is being added or removed in exchange for LP Tokens. * @dev used by Beanstalk to calculate the max deltaB that can be converted in/out of a Well. * @param reserves The reserves of the Well * @param j The index of the reserve to solve for * @param ratios The ratios of reserves to solve for * @param data Well function data provided on every call * @return reserve The resulting reserve at the jth index */ function calcReserveAtRatioLiquidity( uint[] calldata reserves, uint j, uint[] calldata ratios, bytes calldata data ) external pure returns (uint reserve); }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /** * @title Call is the struct that contains the target address and extra calldata of a generic call. */ struct Call { address target; // The address the call is executed on. bytes data; // Extra calldata to be passed during the call } /** * @title IWell is the interface for the Well contract. * * In order for a Well to be verified using a permissionless on-chain registry, a Well Implementation should: * - Not be able to self-destruct (Aquifer's registry would be vulnerable to a metamorphic contract attack) * - Not be able to change its tokens, Well Function, Pumps and Well Data */ interface IWell { /** * @notice Emitted when a Swap occurs. * @param fromToken The token swapped from * @param toToken The token swapped to * @param amountIn The amount of `fromToken` transferred into the Well * @param amountOut The amount of `toToken` transferred out of the Well * @param recipient The address that received `toToken` */ event Swap(IERC20 fromToken, IERC20 toToken, uint256 amountIn, uint256 amountOut, address recipient); /** * @notice Emitted when liquidity is added to the Well. * @param tokenAmountsIn The amount of each token added to the Well * @param lpAmountOut The amount of LP tokens minted * @param recipient The address that received the LP tokens */ event AddLiquidity(uint256[] tokenAmountsIn, uint256 lpAmountOut, address recipient); /** * @notice Emitted when liquidity is removed from the Well as multiple underlying tokens. * @param lpAmountIn The amount of LP tokens burned * @param tokenAmountsOut The amount of each underlying token removed * @param recipient The address that received the underlying tokens * @dev Gas cost scales with `n` tokens. */ event RemoveLiquidity(uint256 lpAmountIn, uint256[] tokenAmountsOut, address recipient); /** * @notice Emitted when liquidity is removed from the Well as a single underlying token. * @param lpAmountIn The amount of LP tokens burned * @param tokenOut The underlying token removed * @param tokenAmountOut The amount of `tokenOut` removed * @param recipient The address that received the underlying tokens * @dev Emitting a separate event when removing liquidity as a single token * saves gas, since `tokenAmountsOut` in {RemoveLiquidity} must emit a value * for each token in the Well. */ event RemoveLiquidityOneToken(uint256 lpAmountIn, IERC20 tokenOut, uint256 tokenAmountOut, address recipient); /** * @notice Emitted when a Shift occurs. * @param reserves The ending reserves after a shift * @param toToken The token swapped to * @param amountOut The amount of `toToken` transferred out of the Well * @param recipient The address that received `toToken` */ event Shift(uint256[] reserves, IERC20 toToken, uint256 amountOut, address recipient); /** * @notice Emitted when a Sync occurs. * @param reserves The ending reserves after a sync * @param lpAmountOut The amount of LP tokens received from the sync. * @param recipient The address that received the LP tokens */ event Sync(uint256[] reserves, uint256 lpAmountOut, address recipient); //////////////////// WELL DEFINITION //////////////////// /** * @notice Returns a list of ERC20 tokens supported by the Well. */ function tokens() external view returns (IERC20[] memory); /** * @notice Returns the Well function as a Call struct. * @dev Contains the address of the Well function contract and extra data to * pass during calls. * * **Well functions** define a relationship between the reserves of the * tokens in the Well and the number of LP tokens. * * A Well function MUST implement {IWellFunction}. */ function wellFunction() external view returns (Call memory); /** * @notice Returns the Pumps attached to the Well as Call structs. * @dev Contains the addresses of the Pumps contract and extra data to pass * during calls. * * **Pumps** are on-chain oracles that are updated every time the Well is * interacted with. * * A Pump is not required for Well operation. For Wells without a Pump: * `pumps().length = 0`. * * An attached Pump MUST implement {IPump}. */ function pumps() external view returns (Call[] memory); /** * @notice Returns the Well data that the Well was bored with. * @dev The existence and signature of Well data is determined by each individual implementation. */ function wellData() external view returns (bytes memory); /** * @notice Returns the Aquifer that created this Well. * @dev Wells can be permissionlessly bored in an Aquifer. * * Aquifers stores the implementation that was used to bore the Well. */ function aquifer() external view returns (address); /** * @notice Returns the tokens, Well Function, Pumps and Well Data associated * with the Well as well as the Aquifer that deployed the Well. */ function well() external view returns ( IERC20[] memory _tokens, Call memory _wellFunction, Call[] memory _pumps, bytes memory _wellData, address _aquifer ); //////////////////// SWAP: FROM //////////////////// /** * @notice Swaps from an exact amount of `fromToken` to a minimum amount of `toToken`. * @param fromToken The token to swap from * @param toToken The token to swap to * @param amountIn The amount of `fromToken` to spend * @param minAmountOut The minimum amount of `toToken` to receive * @param recipient The address to receive `toToken` * @param deadline The timestamp after which this operation is invalid * @return amountOut The amount of `toToken` received */ function swapFrom( IERC20 fromToken, IERC20 toToken, uint256 amountIn, uint256 minAmountOut, address recipient, uint256 deadline ) external returns (uint256 amountOut); /** * @notice Swaps from an exact amount of `fromToken` to a minimum amount of `toToken` and supports fee on transfer tokens. * @param fromToken The token to swap from * @param toToken The token to swap to * @param amountIn The amount of `fromToken` to spend * @param minAmountOut The minimum amount of `toToken` to take from the Well. Note that if `toToken` charges a fee on transfer, `recipient` will receive less than this amount. * @param recipient The address to receive `toToken` * @param deadline The timestamp after which this operation is invalid * @return amountOut The amount of `toToken` transferred from the Well. Note that if `toToken` charges a fee on transfer, `recipient` may receive less than this amount. * @dev Can also be used for tokens without a fee on transfer, but is less gas efficient. */ function swapFromFeeOnTransfer( IERC20 fromToken, IERC20 toToken, uint256 amountIn, uint256 minAmountOut, address recipient, uint256 deadline ) external returns (uint256 amountOut); /** * @notice Gets the amount of one token received for swapping an amount of another token. * @param fromToken The token to swap from * @param toToken The token to swap to * @param amountIn The amount of `fromToken` to spend * @return amountOut The amount of `toToken` to receive */ function getSwapOut(IERC20 fromToken, IERC20 toToken, uint256 amountIn) external view returns (uint256 amountOut); //////////////////// SWAP: TO //////////////////// /** * @notice Swaps from a maximum amount of `fromToken` to an exact amount of `toToken`. * @param fromToken The token to swap from * @param toToken The token to swap to * @param maxAmountIn The maximum amount of `fromToken` to spend * @param amountOut The amount of `toToken` to receive * @param recipient The address to receive `toToken` * @param deadline The timestamp after which this operation is invalid * @return amountIn The amount of `toToken` received */ function swapTo( IERC20 fromToken, IERC20 toToken, uint256 maxAmountIn, uint256 amountOut, address recipient, uint256 deadline ) external returns (uint256 amountIn); /** * @notice Gets the amount of one token that must be spent to receive an amount of another token during a swap. * @param fromToken The token to swap from * @param toToken The token to swap to * @param amountOut The amount of `toToken` desired * @return amountIn The amount of `fromToken` that must be spent */ function getSwapIn(IERC20 fromToken, IERC20 toToken, uint256 amountOut) external view returns (uint256 amountIn); //////////////////// SHIFT //////////////////// /** * @notice Shifts at least `minAmountOut` excess tokens held by the Well into `tokenOut` and delivers to `recipient`. * @param tokenOut The token to shift into * @param minAmountOut The minimum amount of `tokenOut` to receive * @param recipient The address to receive the token * @return amountOut The amount of `tokenOut` received * @dev Can be used in a multicall using a contract like Pipeline to perform gas efficient swaps. * No deadline is needed since this function does not use the user's assets. If adding liquidity in a multicall, * then a deadline check can be added to the multicall. */ function shift(IERC20 tokenOut, uint256 minAmountOut, address recipient) external returns (uint256 amountOut); /** * @notice Calculates the amount of the token out received from shifting excess tokens held by the Well. * @param tokenOut The token to shift into * @return amountOut The amount of `tokenOut` received */ function getShiftOut(IERC20 tokenOut) external returns (uint256 amountOut); //////////////////// ADD LIQUIDITY //////////////////// /** * @notice Adds liquidity to the Well as multiple tokens in any ratio. * @param tokenAmountsIn The amount of each token to add; MUST match the indexing of {Well.tokens} * @param minLpAmountOut The minimum amount of LP tokens to receive * @param recipient The address to receive the LP tokens * @param deadline The timestamp after which this operation is invalid * @return lpAmountOut The amount of LP tokens received */ function addLiquidity( uint256[] memory tokenAmountsIn, uint256 minLpAmountOut, address recipient, uint256 deadline ) external returns (uint256 lpAmountOut); /** * @notice Adds liquidity to the Well as multiple tokens in any ratio and supports * fee on transfer tokens. * @param tokenAmountsIn The amount of each token to add; MUST match the indexing of {Well.tokens} * @param minLpAmountOut The minimum amount of LP tokens to receive * @param recipient The address to receive the LP tokens * @param deadline The timestamp after which this operation is invalid * @return lpAmountOut The amount of LP tokens received * @dev Can also be used for tokens without a fee on transfer, but is less gas efficient. */ function addLiquidityFeeOnTransfer( uint256[] memory tokenAmountsIn, uint256 minLpAmountOut, address recipient, uint256 deadline ) external returns (uint256 lpAmountOut); /** * @notice Gets the amount of LP tokens received from adding liquidity as multiple tokens in any ratio. * @param tokenAmountsIn The amount of each token to add; MUST match the indexing of {Well.tokens} * @return lpAmountOut The amount of LP tokens received */ function getAddLiquidityOut(uint256[] memory tokenAmountsIn) external view returns (uint256 lpAmountOut); //////////////////// REMOVE LIQUIDITY: BALANCED //////////////////// /** * @notice Removes liquidity from the Well as all underlying tokens in a balanced ratio. * @param lpAmountIn The amount of LP tokens to burn * @param minTokenAmountsOut The minimum amount of each underlying token to receive; MUST match the indexing of {Well.tokens} * @param recipient The address to receive the underlying tokens * @param deadline The timestamp after which this operation is invalid * @return tokenAmountsOut The amount of each underlying token received */ function removeLiquidity( uint256 lpAmountIn, uint256[] calldata minTokenAmountsOut, address recipient, uint256 deadline ) external returns (uint256[] memory tokenAmountsOut); /** * @notice Gets the amount of each underlying token received from removing liquidity in a balanced ratio. * @param lpAmountIn The amount of LP tokens to burn * @return tokenAmountsOut The amount of each underlying token received */ function getRemoveLiquidityOut(uint256 lpAmountIn) external view returns (uint256[] memory tokenAmountsOut); //////////////////// REMOVE LIQUIDITY: ONE TOKEN //////////////////// /** * @notice Removes liquidity from the Well as a single underlying token. * @param lpAmountIn The amount of LP tokens to burn * @param tokenOut The underlying token to receive * @param minTokenAmountOut The minimum amount of `tokenOut` to receive * @param recipient The address to receive the underlying tokens * @param deadline The timestamp after which this operation is invalid * @return tokenAmountOut The amount of `tokenOut` received */ function removeLiquidityOneToken( uint256 lpAmountIn, IERC20 tokenOut, uint256 minTokenAmountOut, address recipient, uint256 deadline ) external returns (uint256 tokenAmountOut); /** * @notice Gets the amount received from removing liquidity from the Well as a single underlying token. * @param lpAmountIn The amount of LP tokens to burn * @param tokenOut The underlying token to receive * @return tokenAmountOut The amount of `tokenOut` received * */ function getRemoveLiquidityOneTokenOut( uint256 lpAmountIn, IERC20 tokenOut ) external view returns (uint256 tokenAmountOut); //////////////////// REMOVE LIQUIDITY: IMBALANCED //////////////////// /** * @notice Removes liquidity from the Well as multiple underlying tokens in any ratio. * @param maxLpAmountIn The maximum amount of LP tokens to burn * @param tokenAmountsOut The amount of each underlying token to receive; MUST match the indexing of {Well.tokens} * @param recipient The address to receive the underlying tokens * @return lpAmountIn The amount of LP tokens burned */ function removeLiquidityImbalanced( uint256 maxLpAmountIn, uint256[] calldata tokenAmountsOut, address recipient, uint256 deadline ) external returns (uint256 lpAmountIn); /** * @notice Gets the amount of LP tokens to burn from removing liquidity as multiple underlying tokens in any ratio. * @param tokenAmountsOut The amount of each underlying token to receive; MUST match the indexing of {Well.tokens} * @return lpAmountIn The amount of LP tokens burned */ function getRemoveLiquidityImbalancedIn(uint256[] calldata tokenAmountsOut) external view returns (uint256 lpAmountIn); //////////////////// RESERVES //////////////////// /** * @notice Syncs the Well's reserves with the Well's balances of underlying tokens. If the reserves * increase, mints at least `minLpAmountOut` LP Tokens to `recipient`. * @param recipient The address to receive the LP tokens * @param minLpAmountOut The minimum amount of LP tokens to receive * @return lpAmountOut The amount of LP tokens received * @dev Can be used in a multicall using a contract like Pipeline to perform gas efficient additions of liquidity. * No deadline is needed since this function does not use the user's assets. If adding liquidity in a multicall, * then a deadline check can be added to the multicall. * If `sync` decreases the Well's reserves, then no LP tokens are minted and `lpAmountOut` must be 0. */ function sync(address recipient, uint256 minLpAmountOut) external returns (uint256 lpAmountOut); /** * @notice Calculates the amount of LP Tokens received from syncing the Well's reserves with the Well's balances. * @return lpAmountOut The amount of LP tokens received */ function getSyncOut() external view returns (uint256 lpAmountOut); /** * @notice Sends excess tokens held by the Well to the `recipient`. * @param recipient The address to send the tokens * @return skimAmounts The amount of each token skimmed * @dev No deadline is needed since this function does not use the user's assets. */ function skim(address recipient) external returns (uint256[] memory skimAmounts); /** * @notice Gets the reserves of each token held by the Well. */ function getReserves() external view returns (uint256[] memory reserves); /** * @notice Returns whether or not the Well is initialized if it requires initialization. * If a Well does not require initialization, it should always return `true`. */ function isInitialized() external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; /** * @title IWellFunction * @notice Defines a relationship between token reserves and LP token supply. * @dev Well Functions can contain arbitrary logic, but should be deterministic * if expected to be used alongside a Pump. When interacing with a Well or * Well Function, always verify that the Well Function is valid. */ interface IWellFunction { /** * @notice Calculates the `j`th reserve given a list of `reserves` and `lpTokenSupply`. * @param reserves A list of token reserves. The jth reserve will be ignored, but a placeholder must be provided. * @param j The index of the reserve to solve for * @param lpTokenSupply The supply of LP tokens * @param data Extra Well function data provided on every call * @return reserve The resulting reserve at the jth index * @dev Should round up to ensure that Well reserves are marginally higher to enforce calcLpTokenSupply(...) >= totalSupply() */ function calcReserve( uint[] memory reserves, uint j, uint lpTokenSupply, bytes calldata data ) external view returns (uint reserve); /** * @notice Gets the LP token supply given a list of reserves. * @param reserves A list of token reserves * @param data Extra Well function data provided on every call * @return lpTokenSupply The resulting supply of LP tokens * @dev Should round down to ensure so that the Well Token supply is marignally lower to enforce calcLpTokenSupply(...) >= totalSupply() */ function calcLpTokenSupply( uint[] memory reserves, bytes calldata data ) external view returns (uint lpTokenSupply); /** * @notice Calculates the amount of each reserve token underlying a given amount of LP tokens. * @param lpTokenAmount An amount of LP tokens * @param reserves A list of token reserves * @param lpTokenSupply The current supply of LP tokens * @param data Extra Well function data provided on every call * @return underlyingAmounts The amount of each reserve token that underlies the LP tokens * @dev The constraint totalSupply() <= calcLPTokenSupply(...) must be held in the case where * `lpTokenAmount` LP tokens are burned in exchanged for `underlyingAmounts`. If the constraint * does not hold, then the Well Function is invalid. */ function calcLPTokenUnderlying( uint lpTokenAmount, uint[] memory reserves, uint lpTokenSupply, bytes calldata data ) external view returns (uint[] memory underlyingAmounts); /** * @notice Returns the name of the Well function. */ function name() external view returns (string memory); /** * @notice Returns the symbol of the Well function. */ function symbol() external view returns (string memory); }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; /** * @title ICumulativePump * @notice Provides an interface for Pumps which calculate time-weighted average * reserves through the use of a cumulative reserve. */ interface ICumulativePump { /** * @notice Reads the current cumulative reserves from the Pump * @param well The address of the Well * @param data data specific to the Well * @return cumulativeReserves The cumulative reserves from the Pump */ function readCumulativeReserves( address well, bytes memory data ) external view returns (bytes memory cumulativeReserves); /** * @notice Reads the current cumulative reserves from the Pump * @param well The address of the Well * @param startCumulativeReserves The cumulative reserves to start the TWA from * @param startTimestamp The timestamp to start the TWA from * @param data data specific to the Well * @return twaReserves The time weighted average reserves from start timestamp to now * @return cumulativeReserves The current cumulative reserves from the Pump at the current timestamp */ function readTwaReserves( address well, bytes calldata startCumulativeReserves, uint startTimestamp, bytes memory data ) external view returns (uint[] memory twaReserves, bytes memory cumulativeReserves); }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; /** * @title Instantaneous Pumps provide an Oracle for instantaneous reserves. */ interface IInstantaneousPump { /** * @notice Reads instantaneous reserves from the Pump * @param well The address of the Well * @return reserves The instantaneous balanecs tracked by the Pump */ function readInstantaneousReserves( address well, bytes memory data ) external view returns (uint[] memory reserves); }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; interface IChainlinkAggregator { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); // getRoundData and latestRoundData should both raise "No data present" // if they do not have data to report, instead of returning unset values // which could be misinterpreted as actual reported values. function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /** * @title IBean * @author Publius * @notice Bean Interface */ abstract contract IBean is IERC20 { function burn(uint256 amount) public virtual; function burnFrom(address account, uint256 amount) public virtual; function mint(address account, uint256 amount) public virtual; function symbol() public view virtual returns (string memory); }
// SPDX-License-Identifier: MIT pragma experimental ABIEncoderV2; pragma solidity =0.7.6; interface IBlockBasefee { // Returns the base fee of this block in wei function block_basefee() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma experimental ABIEncoderV2; pragma solidity =0.7.6; interface ICurvePool { function A_precise() external view returns (uint256); function get_balances() external view returns (uint256[2] memory); function totalSupply() external view returns (uint256); function add_liquidity(uint256[2] memory amounts, uint256 min_mint_amount) external returns (uint256); function remove_liquidity_one_coin(uint256 _token_amount, int128 i, uint256 min_amount) external returns (uint256); function balances(int128 i) external view returns (uint256); function fee() external view returns (uint256); function coins(uint256 i) external view returns (address); function get_virtual_price() external view returns (uint256); function calc_token_amount(uint256[2] calldata amounts, bool deposit) external view returns (uint256); function calc_withdraw_one_coin(uint256 _token_amount, int128 i) external view returns (uint256); function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external returns (uint256); function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy) external returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); } interface ICurveZap { function add_liquidity(address _pool, uint256[4] memory _deposit_amounts, uint256 _min_mint_amount) external returns (uint256); function calc_token_amount(address _pool, uint256[4] memory _amounts, bool _is_deposit) external returns (uint256); } interface ICurvePoolR { function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy, address receiver) external returns (uint256); function exchange_underlying(int128 i, int128 j, uint256 dx, uint256 min_dy, address receiver) external returns (uint256); function remove_liquidity_one_coin(uint256 _token_amount, int128 i, uint256 min_amount, address receiver) external returns (uint256); } interface ICurvePool2R { function add_liquidity(uint256[2] memory amounts, uint256 min_mint_amount, address reciever) external returns (uint256); function remove_liquidity(uint256 _burn_amount, uint256[2] memory _min_amounts, address reciever) external returns (uint256[2] calldata); function remove_liquidity_imbalance(uint256[2] memory _amounts, uint256 _max_burn_amount, address reciever) external returns (uint256); } interface ICurvePool3R { function add_liquidity(uint256[3] memory amounts, uint256 min_mint_amount, address reciever) external returns (uint256); function remove_liquidity(uint256 _burn_amount, uint256[3] memory _min_amounts, address reciever) external returns (uint256[3] calldata); function remove_liquidity_imbalance(uint256[3] memory _amounts, uint256 _max_burn_amount, address reciever) external returns (uint256); } interface ICurvePool4R { function add_liquidity(uint256[4] memory amounts, uint256 min_mint_amount, address reciever) external returns (uint256); function remove_liquidity(uint256 _burn_amount, uint256[4] memory _min_amounts, address reciever) external returns (uint256[4] calldata); function remove_liquidity_imbalance(uint256[4] memory _amounts, uint256 _max_burn_amount, address reciever) external returns (uint256); } interface I3Curve { function get_virtual_price() external view returns (uint256); } interface ICurveFactory { function get_coins(address _pool) external view returns (address[4] calldata); function get_underlying_coins(address _pool) external view returns (address[8] calldata); } interface ICurveCryptoFactory { function get_coins(address _pool) external view returns (address[8] calldata); } interface ICurvePoolC { function exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy) external returns (uint256); } interface ICurvePoolNoReturn { function exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy) external; function add_liquidity(uint256[3] memory amounts, uint256 min_mint_amount) external; function remove_liquidity(uint256 _burn_amount, uint256[3] memory _min_amounts) external; function remove_liquidity_imbalance(uint256[3] memory _amounts, uint256 _max_burn_amount) external; function remove_liquidity_one_coin(uint256 _token_amount, uint256 i, uint256 min_amount) external; } interface ICurvePoolNoReturn128 { function exchange(int128 i, int128 j, uint256 dx, uint256 min_dy) external; function remove_liquidity_one_coin(uint256 _token_amount, int128 i, uint256 min_amount) external; }
// SPDX-License-Identifier: MIT pragma experimental ABIEncoderV2; pragma solidity =0.7.6; /******************************************************************************\ * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen) /******************************************************************************/ interface IDiamondCut { enum FacetCutAction {Add, Replace, Remove} struct FacetCut { address facetAddress; FacetCutAction action; bytes4[] functionSelectors; } /// @notice Add/replace/remove any number of functions and optionally execute /// a function with delegatecall /// @param _diamondCut Contains the facet addresses and function selectors /// @param _init The address of the contract or facet to execute _calldata /// @param _calldata A function call, including function selector and arguments /// _calldata is executed with delegatecall on _init function diamondCut( FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata ) external; event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata); }
// SPDX-License-Identifier: MIT pragma experimental ABIEncoderV2; pragma solidity =0.7.6; // A loupe is a small magnifying glass used to look at diamonds. // These functions look at diamonds interface IDiamondLoupe { /// These functions are expected to be called frequently /// by tools. struct Facet { address facetAddress; bytes4[] functionSelectors; } /// @notice Gets all facet addresses and their four byte function selectors. /// @return facets_ Facet function facets() external view returns (Facet[] memory facets_); /// @notice Gets all the function selectors supported by a specific facet. /// @param _facet The facet address. /// @return facetFunctionSelectors_ function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetFunctionSelectors_); /// @notice Get all the facet addresses used by a diamond. /// @return facetAddresses_ function facetAddresses() external view returns (address[] memory facetAddresses_); /// @notice Gets the facet that supports the given selector. /// @dev If facet is not found return address(0). /// @param _functionSelector The function selector. /// @return facetAddress_ The facet address. function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_); }
// SPDX-License-Identifier: MIT pragma experimental ABIEncoderV2; pragma solidity =0.7.6; interface IERC165 { /// @notice Query if a contract implements an interface /// @param interfaceId The interface identifier, as specified in ERC-165 /// @dev Interface identification is specified in ERC-165. This function /// uses less than 30,000 gas. /// @return `true` if the contract implements `interfaceID` and /// `interfaceID` is not 0xffffffff, `false` otherwise function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma experimental ABIEncoderV2; pragma solidity =0.7.6; interface IFertilizer { struct Balance { uint128 amount; uint128 lastBpf; } function beanstalkUpdate( address account, uint256[] memory ids, uint128 bpf ) external returns (uint256); function beanstalkMint(address account, uint256 id, uint128 amount, uint128 bpf) external; function balanceOfFertilized(address account, uint256[] memory ids) external view returns (uint256); function balanceOfUnfertilized(address account, uint256[] memory ids) external view returns (uint256); function lastBalanceOf(address account, uint256 id) external view returns (Balance memory); function lastBalanceOfBatch(address[] memory account, uint256[] memory id) external view returns (Balance[] memory); function setURI(string calldata newuri) external; }
// SPDX-License-Identifier: MIT pragma experimental ABIEncoderV2; pragma solidity =0.7.6; interface IProxyAdmin { function upgrade(address proxy, address implementation) external; }
/* SPDX-License-Identifier: MIT */ pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol"; /** * @title Decimal * @author dYdX * * Library that defines a fixed-point number with 18 decimal places. */ library Decimal { using SafeMath for uint256; // ============ Constants ============ uint256 constant BASE = 10**18; // ============ Structs ============ struct D256 { uint256 value; } // ============ Static Functions ============ function zero() internal pure returns (D256 memory) { return D256({ value: 0 }); } function one() internal pure returns (D256 memory) { return D256({ value: BASE }); } function from( uint256 a ) internal pure returns (D256 memory) { return D256({ value: a.mul(BASE) }); } function ratio( uint256 a, uint256 b ) internal pure returns (D256 memory) { return D256({ value: getPartial(a, BASE, b) }); } // ============ Self Functions ============ function add( D256 memory self, uint256 b ) internal pure returns (D256 memory) { return D256({ value: self.value.add(b.mul(BASE)) }); } function sub( D256 memory self, uint256 b ) internal pure returns (D256 memory) { return D256({ value: self.value.sub(b.mul(BASE)) }); } function sub( D256 memory self, uint256 b, string memory reason ) internal pure returns (D256 memory) { return D256({ value: self.value.sub(b.mul(BASE), reason) }); } function mul( D256 memory self, uint256 b ) internal pure returns (D256 memory) { return D256({ value: self.value.mul(b) }); } function div( D256 memory self, uint256 b ) internal pure returns (D256 memory) { return D256({ value: self.value.div(b) }); } function pow( D256 memory self, uint256 b ) internal pure returns (D256 memory) { if (b == 0) { return one(); } D256 memory temp = D256({ value: self.value }); for (uint256 i = 1; i < b; ++i) { temp = mul(temp, self); } return temp; } function add( D256 memory self, D256 memory b ) internal pure returns (D256 memory) { return D256({ value: self.value.add(b.value) }); } function sub( D256 memory self, D256 memory b ) internal pure returns (D256 memory) { return D256({ value: self.value.sub(b.value) }); } function sub( D256 memory self, D256 memory b, string memory reason ) internal pure returns (D256 memory) { return D256({ value: self.value.sub(b.value, reason) }); } function mul( D256 memory self, D256 memory b ) internal pure returns (D256 memory) { return D256({ value: getPartial(self.value, b.value, BASE) }); } function div( D256 memory self, D256 memory b ) internal pure returns (D256 memory) { return D256({ value: getPartial(self.value, BASE, b.value) }); } function equals(D256 memory self, D256 memory b) internal pure returns (bool) { return self.value == b.value; } function greaterThan(D256 memory self, D256 memory b) internal pure returns (bool) { return compareTo(self, b) == 2; } function lessThan(D256 memory self, D256 memory b) internal pure returns (bool) { return compareTo(self, b) == 0; } function greaterThanOrEqualTo(D256 memory self, D256 memory b) internal pure returns (bool) { return compareTo(self, b) > 0; } function lessThanOrEqualTo(D256 memory self, D256 memory b) internal pure returns (bool) { return compareTo(self, b) < 2; } function isZero(D256 memory self) internal pure returns (bool) { return self.value == 0; } function asUint256(D256 memory self) internal pure returns (uint256) { return self.value.div(BASE); } // ============ Core Methods ============ function getPartial( uint256 target, uint256 numerator, uint256 denominator ) private pure returns (uint256) { return target.mul(numerator).div(denominator); } function compareTo( D256 memory a, D256 memory b ) private pure returns (uint256) { if (a.value == b.value) { return 1; } return a.value > b.value ? 2 : 0; } }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; // Import all of AppStorage to give importers of LibAppStorage access to {Account}, etc. import "../beanstalk/AppStorage.sol"; /** * @title LibAppStorage * @author Publius * @notice Allows libaries to access Beanstalk's state. */ library LibAppStorage { function diamondStorage() internal pure returns (AppStorage storage ds) { assembly { ds.slot := 0 } } }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {IWell} from "contracts/interfaces/basin/IWell.sol"; import {C} from "contracts/C.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {AppStorage, LibAppStorage} from "contracts/libraries/LibAppStorage.sol"; /** * @title LibBarnRaise * @author Brendan * @notice Library fetching Barn Raise Token */ library LibBarnRaise { function getBarnRaiseToken() internal view returns (address) { IERC20[] memory tokens = IWell(getBarnRaiseWell()).tokens(); return address(address(tokens[0]) == C.BEAN ? tokens[1] : tokens[0]); } function getBarnRaiseWell() internal view returns (address) { AppStorage storage s = LibAppStorage.diamondStorage(); return s.u[C.UNRIPE_LP].underlyingToken == address(0) ? C.BEAN_WSTETH_WELL : s.u[C.UNRIPE_LP].underlyingToken; } }
/** * SPDX-License-Identifier: MIT **/ pragma solidity =0.7.6; /* * @author: Malteasy * @title: LibBytes */ library LibBytes { /* * @notice From Solidity Bytes Arrays Utils * @author Gonçalo Sá <[email protected]> */ function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { require(_start + 1 >= _start, "toUint8_overflow"); require(_bytes.length >= _start + 1 , "toUint8_outOfBounds"); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } /* * @notice From Solidity Bytes Arrays Utils * @author Gonçalo Sá <[email protected]> */ function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) { require(_start + 4 >= _start, "toUint32_overflow"); require(_bytes.length >= _start + 4, "toUint32_outOfBounds"); uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } /* * @notice From Solidity Bytes Arrays Utils * @author Gonçalo Sá <[email protected]> */ function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { require(_start + 32 >= _start, "toUint256_overflow"); require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } /** * @notice Loads a slice of a calldata bytes array into memory * @param b The calldata bytes array to load from * @param start The start of the slice * @param length The length of the slice */ function sliceToMemory(bytes calldata b, uint256 start, uint256 length) internal pure returns (bytes memory) { bytes memory memBytes = new bytes(length); for(uint256 i = 0; i < length; ++i) { memBytes[i] = b[start + i]; } return memBytes; } function packAddressAndStem(address _address, int96 stem) internal pure returns (uint256) { return uint256(_address) << 96 | uint96(stem); } function unpackAddressAndStem(uint256 data) internal pure returns(address, int96) { return (address(uint160(data >> 96)), int96(int256(data))); } }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; import {LibAppStorage, AppStorage} from "./LibAppStorage.sol"; /** * @author Brean * @title LibCases handles the cases for beanstalk. * * @dev Cases are used to determine the change in * temperature and Bean to maxLP gaugePoint per BDV ratio. * * Data format: * * mT: 4 Bytes (1% = 1e6) * bT: 1 Bytes (1% = 1) * mL: 10 Bytes (1% = 1e18) * bL: 10 Bytes (1% = 1e18) * 7 bytes are left for future use. * * Temperature and Bean and maxLP gaugePoint per BDV ratio is updated as such: * T_n = mT * T_n-1 + bT * L_n = mL * L_n-1 + bL * * In total, there are 144 cases (4 * 3 * 3 * 4) * * temperature is stored in AppStorage with 0 decimal precision (1% = 1), * which is why bT has 0 decimal precision. * */ library LibCases { struct CaseData { uint32 mT; int8 bT; uint80 mL; int80 bL; } // constants are used for reability purposes, // given that multiple cases use the same values. // // Naming Convention: // PLUS: increment by X (y_i = y_1 + X) // MINUS decrement by X (y_i = y_1 - X) // INCR: scale up by X (y_i = y_1 * X) // DECR: scale down by X (y_i = y_1 * (1-X)) // T: Temperature, L: Bean to max LP gauge point per BDV ratio // Example: T_PLUS_3_L_MINUS_FIFTY-> Temperature is incremented 3%, // BeantoMaxLPGaugePointPerBdvRatio is decrement by 50%. // bT ////////////////////////////////////////////////////////// [ mT ][][ mL ][ BL ][ null ] bytes32 internal constant T_PLUS_3_L_MINUS_FIFTY = bytes32(0x05F5E1000300056BC75E2D63100000FFFD4A1C50E94E78000000000000000000); // temperature increased by 3%, Bean2maxLpGpPerBdv decreased by 50. bytes32 internal constant T_PLUS_1_L_MINUS_FIFTY = bytes32(0x05F5E1000100056BC75E2D63100000FFFD4A1C50E94E78000000000000000000); // temperature increased by 1%, Bean2maxLpGpPerBdv decreased by 50. bytes32 internal constant T_PLUS_0_L_MINUS_FIFTY = bytes32(0x05F5E1000000056BC75E2D63100000FFFD4A1C50E94E78000000000000000000); // temperature increased by 0%, Bean2maxLpGpPerBdv decreased by 50. bytes32 internal constant T_MINUS_1_L_MINUS_FIFTY = bytes32(0x05F5E100FF00056BC75E2D63100000FFFD4A1C50E94E78000000000000000000); // temperature decreased by 1%, Bean2maxLpGpPerBdv decreased by 50. bytes32 internal constant T_MINUS_3_L_MINUS_FIFTY = bytes32(0x05F5E100FD00056BC75E2D63100000FFFD4A1C50E94E78000000000000000000); // temperature decreased by 3%, Bean2maxLpGpPerBdv decreased by 50. ////////////////////////////////////////////////////////// [ mT ][][ mL ][ BL ][ null ] bytes32 internal constant T_PLUS_3_L_PLUS_ONE = bytes32(0x05F5E1000300056BC75E2D6310000000000DE0B6B3A764000000000000000000); // temperature increased by 3%, Bean2maxLpGpPerBdv increased by 1. bytes32 internal constant T_PLUS_1_L_PLUS_ONE = bytes32(0x05F5E1000100056BC75E2D6310000000000DE0B6B3A764000000000000000000); // temperature increased by 1%, Bean2maxLpGpPerBdv increased by 1. bytes32 internal constant T_PLUS_0_L_PLUS_ONE = bytes32(0x05F5E1000000056BC75E2D6310000000000DE0B6B3A764000000000000000000); // temperature increased by 0%, Bean2maxLpGpPerBdv increased by 1. ////////////////////////////////////////////////////////// [ mT ][][ mL ][ BL ][ null ] bytes32 internal constant T_PLUS_3_L_PLUS_TWO = bytes32(0x05F5E1000300056BC75E2D6310000000001BC16D674EC8000000000000000000); // temperature increased by 3%, Bean2maxLpGpPerBdv increased by 2. bytes32 internal constant T_PLUS_1_L_PLUS_TWO = bytes32(0x05F5E1000100056BC75E2D6310000000001BC16D674EC8000000000000000000); // temperature increased by 1%, Bean2maxLpGpPerBdv increased by 2. bytes32 internal constant T_PLUS_0_L_PLUS_TWO = bytes32(0x05F5E1000000056BC75E2D6310000000001BC16D674EC8000000000000000000); // temperature increased by 0%, Bean2maxLpGpPerBdv increased by 2. ////////////////////////////////////////////////////////// [ mT ][][ mL ][ BL ][ null ] bytes32 internal constant T_PLUS_0_L_MINUS_ONE = bytes32(0x05F5E1000000056BC75E2D63100000FFFFF21F494C589C000000000000000000); // temperature increased by 0%, Bean2maxLpGpPerBdv decreased by 1. bytes32 internal constant T_MINUS_1_L_MINUS_ONE = bytes32(0x05F5E100FF00056BC75E2D63100000FFFFF21F494C589C000000000000000000); // temperature decreased by 1%, Bean2maxLpGpPerBdv decreased by 1. bytes32 internal constant T_MINUS_3_L_MINUS_ONE = bytes32(0x05F5E100FD00056BC75E2D63100000FFFFF21F494C589C000000000000000000); // temperature decreased by 3%, Bean2maxLpGpPerBdv decreased by 1. /** * @notice given a caseID (0-144), return the caseData. * * CaseV2 allows developers to change both the absolute * and relative change in temperature and bean to maxLP gaugePoint to BDV ratio, * with greater precision than CaseV1. * */ function getDataFromCase(uint256 caseId) internal view returns (bytes32 caseData) { AppStorage storage s = LibAppStorage.diamondStorage(); return s.casesV2[caseId]; } /** * @notice given a caseID (0-144), return the data associated. * @dev * Each case outputs 4 variables: * mT: Relative Temperature change. (1% = 1e6) * bT: Absolute Temperature change. (1% = 1) * mL: Relative Grown Stalk to Liquidity change. (1% = 1e18) * bL: Absolute Grown Stalk to Liquidity change. (1% = 1e18) */ function decodeCaseData(uint256 caseId) internal view returns (CaseData memory cd) { bytes32 _caseData = getDataFromCase(caseId); // cd.mT = uint32(bytes4(_caseData)); Uncomment if you want to use mT cd.bT = int8(bytes1(_caseData << 32)); // cd.mL = uint80(bytes10(_caseData << 40)); Uncomment if you want to use mL cd.bL = int80(bytes10(_caseData << 120)); } function setCasesV2() internal { AppStorage storage s = LibAppStorage.diamondStorage(); s.casesV2 = [ // Dsc soil demand, Steady soil demand Inc soil demand /////////////////////// Exremely Low L2SR /////////////////////// bytes32(T_PLUS_3_L_MINUS_FIFTY), T_PLUS_3_L_MINUS_FIFTY, T_PLUS_1_L_MINUS_FIFTY, // Exs Low: P < 1 T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > 1 T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > Q T_PLUS_3_L_MINUS_FIFTY, T_PLUS_3_L_MINUS_FIFTY, T_PLUS_1_L_MINUS_FIFTY, // Rea Low: P < 1 T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > 1 T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > Q T_PLUS_3_L_MINUS_FIFTY, T_PLUS_1_L_MINUS_FIFTY, T_PLUS_0_L_MINUS_FIFTY, // Rea Hgh: P < 1 T_PLUS_0_L_MINUS_FIFTY, T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > 1 T_PLUS_0_L_MINUS_FIFTY, T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > Q T_PLUS_3_L_MINUS_FIFTY, T_PLUS_1_L_MINUS_FIFTY, T_PLUS_0_L_MINUS_FIFTY, // Exs Hgh: P < 1 T_PLUS_0_L_MINUS_FIFTY, T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > 1 T_PLUS_0_L_MINUS_FIFTY, T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > Q /////////////////////// Reasonably Low L2SR /////////////////////// T_PLUS_3_L_MINUS_FIFTY, T_PLUS_3_L_MINUS_FIFTY, T_PLUS_1_L_MINUS_FIFTY, // Exs Low: P < 1 T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > 1 T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > Q T_PLUS_3_L_MINUS_FIFTY, T_PLUS_3_L_MINUS_FIFTY, T_PLUS_1_L_MINUS_FIFTY, // Rea Low: P < 1 T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > 1 T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > Q T_PLUS_3_L_PLUS_ONE, T_PLUS_1_L_PLUS_ONE, T_PLUS_0_L_PLUS_ONE, // Rea Hgh: P < 1 T_PLUS_0_L_MINUS_ONE, T_MINUS_1_L_MINUS_ONE, T_MINUS_3_L_MINUS_ONE, // P > 1 T_PLUS_0_L_MINUS_FIFTY, T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > Q T_PLUS_3_L_PLUS_ONE, T_PLUS_1_L_PLUS_ONE, T_PLUS_0_L_PLUS_ONE, // Exs Hgh: P < 1 T_PLUS_0_L_MINUS_ONE, T_MINUS_1_L_MINUS_ONE, T_MINUS_3_L_MINUS_ONE, // P > 1 T_PLUS_0_L_MINUS_FIFTY, T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > Q /////////////////////// Reasonably High L2SR /////////////////////// T_PLUS_3_L_PLUS_ONE, T_PLUS_3_L_PLUS_ONE, T_PLUS_1_L_PLUS_ONE, // Exs Low: P < 1 T_MINUS_1_L_MINUS_ONE, T_MINUS_3_L_MINUS_ONE, T_MINUS_3_L_MINUS_ONE, // P > 1 T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > Q T_PLUS_3_L_PLUS_ONE, T_PLUS_3_L_PLUS_ONE, T_PLUS_1_L_PLUS_ONE, // Rea Low: P < 1 T_MINUS_1_L_MINUS_ONE, T_MINUS_3_L_MINUS_ONE, T_MINUS_3_L_MINUS_ONE, // P > 1 T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > Q T_PLUS_3_L_PLUS_ONE, T_PLUS_1_L_PLUS_ONE, T_PLUS_0_L_PLUS_ONE, // Rea Hgh: P < 1 T_PLUS_0_L_MINUS_ONE, T_MINUS_1_L_MINUS_ONE, T_MINUS_3_L_MINUS_ONE, // P > 1 T_PLUS_0_L_MINUS_FIFTY, T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > Q T_PLUS_3_L_PLUS_ONE, T_PLUS_1_L_PLUS_ONE, T_PLUS_0_L_PLUS_ONE, // Exs Hgh: P < 1 T_PLUS_0_L_MINUS_ONE, T_MINUS_1_L_MINUS_ONE, T_MINUS_3_L_MINUS_ONE, // P > 1 T_PLUS_0_L_MINUS_FIFTY, T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > Q /////////////////////// Extremely High L2SR /////////////////////// T_PLUS_3_L_PLUS_ONE, T_PLUS_3_L_PLUS_ONE, T_PLUS_1_L_PLUS_ONE, // Exs Low: P < 1 T_MINUS_1_L_MINUS_ONE, T_MINUS_3_L_MINUS_ONE, T_MINUS_3_L_MINUS_ONE, // P > 1 T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > Q T_PLUS_3_L_PLUS_ONE, T_PLUS_3_L_PLUS_ONE, T_PLUS_1_L_PLUS_ONE, // Rea Low: P < 1 T_MINUS_1_L_MINUS_ONE, T_MINUS_3_L_MINUS_ONE, T_MINUS_3_L_MINUS_ONE, // P > 1 T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > Q T_PLUS_3_L_PLUS_TWO, T_PLUS_1_L_PLUS_TWO, T_PLUS_0_L_PLUS_TWO, // Rea Hgh: P < 1 T_PLUS_0_L_MINUS_ONE, T_MINUS_1_L_MINUS_ONE, T_MINUS_3_L_MINUS_ONE, // P > 1 T_PLUS_0_L_MINUS_FIFTY, T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY, // P > Q T_PLUS_3_L_PLUS_TWO, T_PLUS_1_L_PLUS_TWO, T_PLUS_0_L_PLUS_TWO, // Exs Hgh: P < 1 T_PLUS_0_L_MINUS_ONE, T_MINUS_1_L_MINUS_ONE, T_MINUS_3_L_MINUS_ONE, // P > 1 T_PLUS_0_L_MINUS_FIFTY, T_MINUS_1_L_MINUS_FIFTY, T_MINUS_3_L_MINUS_FIFTY // P > Q ]; } }
/* SPDX-License-Identifier: MIT */ pragma experimental ABIEncoderV2; pragma solidity =0.7.6; /******************************************************************************\ * Author: Nick Mudge <[email protected]> (https://twitter.com/mudgen) * EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535 /******************************************************************************/ import {IDiamondCut} from "../interfaces/IDiamondCut.sol"; import {IDiamondLoupe} from "../interfaces/IDiamondLoupe.sol"; import {IERC165} from "../interfaces/IERC165.sol"; library LibDiamond { bytes32 constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage"); struct FacetAddressAndPosition { address facetAddress; uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array } struct FacetFunctionSelectors { bytes4[] functionSelectors; uint256 facetAddressPosition; // position of facetAddress in facetAddresses array } struct DiamondStorage { // maps function selector to the facet address and // the position of the selector in the facetFunctionSelectors.selectors array mapping(bytes4 => FacetAddressAndPosition) selectorToFacetAndPosition; // maps facet addresses to function selectors mapping(address => FacetFunctionSelectors) facetFunctionSelectors; // facet addresses address[] facetAddresses; // Used to query if a contract implements an interface. // Used to implement ERC-165. mapping(bytes4 => bool) supportedInterfaces; // owner of the contract address contractOwner; } function diamondStorage() internal pure returns (DiamondStorage storage ds) { bytes32 position = DIAMOND_STORAGE_POSITION; assembly { ds.slot := position } } event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); function setContractOwner(address _newOwner) internal { DiamondStorage storage ds = diamondStorage(); address previousOwner = ds.contractOwner; ds.contractOwner = _newOwner; emit OwnershipTransferred(previousOwner, _newOwner); } function contractOwner() internal view returns (address contractOwner_) { contractOwner_ = diamondStorage().contractOwner; } function enforceIsOwnerOrContract() internal view { require(msg.sender == diamondStorage().contractOwner || msg.sender == address(this), "LibDiamond: Must be contract or owner" ); } function enforceIsContractOwner() internal view { require(msg.sender == diamondStorage().contractOwner, "LibDiamond: Must be contract owner"); } event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata); function addDiamondFunctions( address _diamondCutFacet, address _diamondLoupeFacet ) internal { IDiamondCut.FacetCut[] memory cut = new IDiamondCut.FacetCut[](2); bytes4[] memory functionSelectors = new bytes4[](1); functionSelectors[0] = IDiamondCut.diamondCut.selector; cut[0] = IDiamondCut.FacetCut({facetAddress: _diamondCutFacet, action: IDiamondCut.FacetCutAction.Add, functionSelectors: functionSelectors}); functionSelectors = new bytes4[](5); functionSelectors[0] = IDiamondLoupe.facets.selector; functionSelectors[1] = IDiamondLoupe.facetFunctionSelectors.selector; functionSelectors[2] = IDiamondLoupe.facetAddresses.selector; functionSelectors[3] = IDiamondLoupe.facetAddress.selector; functionSelectors[4] = IERC165.supportsInterface.selector; cut[1] = IDiamondCut.FacetCut({ facetAddress: _diamondLoupeFacet, action: IDiamondCut.FacetCutAction.Add, functionSelectors: functionSelectors }); diamondCut(cut, address(0), ""); } // Internal function version of diamondCut function diamondCut( IDiamondCut.FacetCut[] memory _diamondCut, address _init, bytes memory _calldata ) internal { for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) { IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action; if (action == IDiamondCut.FacetCutAction.Add) { addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else if (action == IDiamondCut.FacetCutAction.Replace) { replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else if (action == IDiamondCut.FacetCutAction.Remove) { removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors); } else { revert("LibDiamondCut: Incorrect FacetCutAction"); } } emit DiamondCut(_diamondCut, _init, _calldata); initializeDiamondCut(_init, _calldata); } function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)"); uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length); // add new facet address if it does not exist if (selectorPosition == 0) { addFacet(ds, _facetAddress); } for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; require(oldFacetAddress == address(0), "LibDiamondCut: Can't add function that already exists"); addFunction(ds, selector, selectorPosition, _facetAddress); selectorPosition++; } } function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); require(_facetAddress != address(0), "LibDiamondCut: Add facet can't be address(0)"); uint96 selectorPosition = uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length); // add new facet address if it does not exist if (selectorPosition == 0) { addFacet(ds, _facetAddress); } for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; require(oldFacetAddress != _facetAddress, "LibDiamondCut: Can't replace function with same function"); removeFunction(ds, oldFacetAddress, selector); addFunction(ds, selector, selectorPosition, _facetAddress); selectorPosition++; } } function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { require(_functionSelectors.length > 0, "LibDiamondCut: No selectors in facet to cut"); DiamondStorage storage ds = diamondStorage(); // if function does not exist then do nothing and return require(_facetAddress == address(0), "LibDiamondCut: Remove facet address must be address(0)"); for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress; removeFunction(ds, oldFacetAddress, selector); } } function addFacet(DiamondStorage storage ds, address _facetAddress) internal { enforceHasContractCode(_facetAddress, "LibDiamondCut: New facet has no code"); ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds.facetAddresses.length; ds.facetAddresses.push(_facetAddress); } function addFunction(DiamondStorage storage ds, bytes4 _selector, uint96 _selectorPosition, address _facetAddress) internal { ds.selectorToFacetAndPosition[_selector].functionSelectorPosition = _selectorPosition; ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(_selector); ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress; } function removeFunction(DiamondStorage storage ds, address _facetAddress, bytes4 _selector) internal { require(_facetAddress != address(0), "LibDiamondCut: Can't remove function that doesn't exist"); // an immutable function is a function defined directly in a diamond require(_facetAddress != address(this), "LibDiamondCut: Can't remove immutable function"); // replace selector with last selector, then delete last selector uint256 selectorPosition = ds.selectorToFacetAndPosition[_selector].functionSelectorPosition; uint256 lastSelectorPosition = ds.facetFunctionSelectors[_facetAddress].functionSelectors.length - 1; // if not the same then replace _selector with lastSelector if (selectorPosition != lastSelectorPosition) { bytes4 lastSelector = ds.facetFunctionSelectors[_facetAddress].functionSelectors[lastSelectorPosition]; ds.facetFunctionSelectors[_facetAddress].functionSelectors[selectorPosition] = lastSelector; ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition = uint96(selectorPosition); } // delete the last selector ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop(); delete ds.selectorToFacetAndPosition[_selector]; // if no more selectors for facet address then delete the facet address if (lastSelectorPosition == 0) { // replace facet address with last facet address and delete last facet address uint256 lastFacetAddressPosition = ds.facetAddresses.length - 1; uint256 facetAddressPosition = ds.facetFunctionSelectors[_facetAddress].facetAddressPosition; if (facetAddressPosition != lastFacetAddressPosition) { address lastFacetAddress = ds.facetAddresses[lastFacetAddressPosition]; ds.facetAddresses[facetAddressPosition] = lastFacetAddress; ds.facetFunctionSelectors[lastFacetAddress].facetAddressPosition = facetAddressPosition; } ds.facetAddresses.pop(); delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition; } } function initializeDiamondCut(address _init, bytes memory _calldata) internal { if (_init == address(0)) { require(_calldata.length == 0, "LibDiamondCut: _init is address(0) but_calldata is not empty"); } else { require(_calldata.length > 0, "LibDiamondCut: _calldata is empty but _init is not address(0)"); if (_init != address(this)) { enforceHasContractCode(_init, "LibDiamondCut: _init address has no code"); } (bool success, bytes memory error) = _init.delegatecall(_calldata); if (!success) { if (error.length > 0) { // bubble up the error revert(string(error)); } else { revert("LibDiamondCut: _init function reverted"); } } } } function enforceHasContractCode(address _contract, string memory _errorMessage) internal view { uint256 contractSize; assembly { contractSize := extcodesize(_contract) } require(contractSize > 0, _errorMessage); } }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {LibAppStorage, AppStorage} from "./LibAppStorage.sol"; import {Decimal, SafeMath} from "contracts/libraries/Decimal.sol"; import {LibWhitelistedTokens, C} from "contracts/libraries/Silo/LibWhitelistedTokens.sol"; import {LibUnripe} from "contracts/libraries/LibUnripe.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {LibSafeMath32} from "contracts/libraries/LibSafeMath32.sol"; import {LibWell} from "contracts/libraries/Well/LibWell.sol"; import {LibBarnRaise} from "contracts/libraries/LibBarnRaise.sol"; /** * @author Brean * @title LibEvaluate calculates the caseId based on the state of Beanstalk. * @dev the current parameters that beanstalk uses to evaluate its state are: * - DeltaB, the amount of Beans needed to be bought/sold to reach peg. * - PodRate, the ratio of Pods outstanding against the bean supply. * - Delta Soil demand, the change in demand of Soil between the current and previous Season. * - LpToSupplyRatio (L2SR), the ratio of liquidity to the circulating Bean supply. * * based on the caseId, Beanstalk adjusts: * - the Temperature * - the ratio of the gaugePoints per BDV of bean and the largest GpPerBdv for a given LP token. */ library DecimalExtended { uint256 private constant PERCENT_BASE = 1e18; function toDecimal(uint256 a) internal pure returns (Decimal.D256 memory) { return Decimal.D256({ value: a }); } } library LibEvaluate { using SafeMath for uint256; using DecimalExtended for uint256; using Decimal for Decimal.D256; using LibSafeMath32 for uint32; // Pod rate bounds uint256 internal constant POD_RATE_LOWER_BOUND = 0.05e18; // 5% uint256 internal constant POD_RATE_OPTIMAL = 0.15e18; // 15% uint256 internal constant POD_RATE_UPPER_BOUND = 0.25e18; // 25% // Change in Soil demand bounds uint256 internal constant DELTA_POD_DEMAND_LOWER_BOUND = 0.95e18; // 95% uint256 internal constant DELTA_POD_DEMAND_UPPER_BOUND = 1.05e18; // 105% /// @dev If all Soil is Sown faster than this, Beanstalk considers demand for Soil to be increasing. uint256 internal constant SOW_TIME_DEMAND_INCR = 600; // seconds uint32 internal constant SOW_TIME_STEADY = 60; // seconds // Liquidity to supply ratio bounds uint256 internal constant LP_TO_SUPPLY_RATIO_UPPER_BOUND = 0.8e18; // 80% uint256 internal constant LP_TO_SUPPLY_RATIO_OPTIMAL = 0.4e18; // 40% uint256 internal constant LP_TO_SUPPLY_RATIO_LOWER_BOUND = 0.12e18; // 12% // Excessive price threshold constant uint256 internal constant EXCESSIVE_PRICE_THRESHOLD = 1.05e6; uint256 internal constant LIQUIDITY_PRECISION = 1e12; struct BeanstalkState { Decimal.D256 deltaPodDemand; Decimal.D256 lpToSupplyRatio; Decimal.D256 podRate; address largestLiqWell; bool oracleFailure; } /** * @notice evaluates the pod rate and returns the caseId * @param podRate the length of the podline (debt), divided by the bean supply. */ function evalPodRate(Decimal.D256 memory podRate) internal pure returns (uint256 caseId) { if (podRate.greaterThanOrEqualTo(POD_RATE_UPPER_BOUND.toDecimal())) { caseId = 27; } else if (podRate.greaterThanOrEqualTo(POD_RATE_OPTIMAL.toDecimal())) { caseId = 18; } else if (podRate.greaterThanOrEqualTo(POD_RATE_LOWER_BOUND.toDecimal())) { caseId = 9; } } /** * @notice updates the caseId based on the price of bean (deltaB) * @param deltaB the amount of beans needed to be sold or bought to get bean to peg. * @param well the well address to get the bean price from. */ function evalPrice( int256 deltaB, address well ) internal view returns (uint256 caseId) { // p > 1 if (deltaB > 0) { // Beanstalk will only use the largest liquidity well to compute the Bean price, // and thus will skip the p > EXCESSIVE_PRICE_THRESHOLD check if the well oracle fails to // compute a valid price this Season. // deltaB > 0 implies that address(well) != address(0). uint256 beanTokenPrice = LibWell.getBeanTokenPriceFromTwaReserves(well); if (beanTokenPrice > 1) { // USD/TOKEN * TOKEN/BEAN = USD/BEAN // 1/USD/BEAN = BEAN/USD uint256 beanUsdPrice = uint256(1e30).div( LibWell.getUsdTokenPriceForWell(well).mul(beanTokenPrice) ); if (beanUsdPrice > EXCESSIVE_PRICE_THRESHOLD) { // p > EXCESSIVE_PRICE_THRESHOLD return caseId = 6; } } caseId = 3; } // p < 1 } /** * @notice Updates the caseId based on the change in Soil demand. * @param deltaPodDemand The change in Soil demand from the previous Season. */ function evalDeltaPodDemand( Decimal.D256 memory deltaPodDemand ) internal pure returns (uint256 caseId) { // increasing if (deltaPodDemand.greaterThanOrEqualTo(DELTA_POD_DEMAND_UPPER_BOUND.toDecimal())) { caseId = 2; // steady } else if (deltaPodDemand.greaterThanOrEqualTo(DELTA_POD_DEMAND_LOWER_BOUND.toDecimal())) { caseId = 1; } // decreasing (caseId = 0) } /** * @notice Evaluates the lp to supply ratio and returns the caseId. * @param lpToSupplyRatio The ratio of liquidity to supply. * * @dev 'liquidity' is definied as the non-bean value in a pool that trades beans. */ function evalLpToSupplyRatio( Decimal.D256 memory lpToSupplyRatio ) internal pure returns (uint256 caseId) { // Extremely High if (lpToSupplyRatio.greaterThanOrEqualTo(LP_TO_SUPPLY_RATIO_UPPER_BOUND.toDecimal())) { caseId = 108; // Reasonably High } else if (lpToSupplyRatio.greaterThanOrEqualTo(LP_TO_SUPPLY_RATIO_OPTIMAL.toDecimal())) { caseId = 72; // Reasonably Low } else if ( lpToSupplyRatio.greaterThanOrEqualTo(LP_TO_SUPPLY_RATIO_LOWER_BOUND.toDecimal()) ) { caseId = 36; } // excessively low (caseId = 0) } /** * @notice Calculates the change in soil demand from the previous season. * @param dsoil The amount of soil sown this season. */ function calcDeltaPodDemand( uint256 dsoil ) internal view returns (Decimal.D256 memory deltaPodDemand, uint32 lastSowTime, uint32 thisSowTime) { AppStorage storage s = LibAppStorage.diamondStorage(); // `s.w.thisSowTime` is set to the number of seconds in it took for // Soil to sell out during the current Season. If Soil didn't sell out, // it remains `type(uint32).max`. if (s.w.thisSowTime < type(uint32).max) { if ( s.w.lastSowTime == type(uint32).max || // Didn't Sow all last Season s.w.thisSowTime < SOW_TIME_DEMAND_INCR || // Sow'd all instantly this Season (s.w.lastSowTime > SOW_TIME_STEADY && s.w.thisSowTime < s.w.lastSowTime.sub(SOW_TIME_STEADY)) // Sow'd all faster ) { deltaPodDemand = Decimal.from(1e18); } else if (s.w.thisSowTime <= s.w.lastSowTime.add(SOW_TIME_STEADY)) { // Sow'd all in same time deltaPodDemand = Decimal.one(); } else { deltaPodDemand = Decimal.zero(); } } else { // Soil didn't sell out uint256 lastDSoil = s.w.lastDSoil; if (dsoil == 0) { deltaPodDemand = Decimal.zero(); // If no one Sow'd } else if (lastDSoil == 0) { deltaPodDemand = Decimal.from(1e18); // If no one Sow'd last Season } else { deltaPodDemand = Decimal.ratio(dsoil, lastDSoil); } } lastSowTime = s.w.thisSowTime; // Overwrite last Season thisSowTime = type(uint32).max; // Reset for next Season } /** * @notice Calculates the liquidity to supply ratio, where liquidity is measured in USD. * @param beanSupply The total supply of Beans. * corresponding to the well addresses in the whitelist. * @dev No support for non-well AMMs at this time. */ function calcLPToSupplyRatio( uint256 beanSupply ) internal view returns (Decimal.D256 memory lpToSupplyRatio, address largestLiqWell, bool oracleFailure) { AppStorage storage s = LibAppStorage.diamondStorage(); // prevent infinite L2SR if (beanSupply == 0) return (Decimal.zero(), address(0), true); address[] memory pools = LibWhitelistedTokens.getWhitelistedLpTokens(); uint256[] memory twaReserves; uint256 totalUsdLiquidity; uint256 largestLiq; uint256 wellLiquidity; for (uint256 i; i < pools.length; i++) { // get the non-bean value in an LP. twaReserves = LibWell.getTwaReservesFromStorageOrBeanstalkPump(pools[i]); // calculate the non-bean usd liquidity value. uint256 usdLiquidity = LibWell.getWellTwaUsdLiquidityFromReserves( pools[i], twaReserves ); // if the usdLiquidty is 0, beanstalk assumes oracle failure. if (usdLiquidity == 0) { oracleFailure = true; } // calculate the scaled, non-bean liquidity in the pool. wellLiquidity = getLiquidityWeight(s.ss[pools[i]].lwSelector).mul(usdLiquidity).div(1e18); // if the liquidity is the largest, update `largestLiqWell`, // and add the liquidity to the total. // `largestLiqWell` is only used to initialize `s.sopWell` upon a sop, // if it has not been initialized. // A hot storage load to skip the block below is significantly more expensive // than performing the logic on every sunrise. if (wellLiquidity > largestLiq) { largestLiq = wellLiquidity; largestLiqWell = pools[i]; } totalUsdLiquidity = totalUsdLiquidity.add(wellLiquidity); if (pools[i] == LibBarnRaise.getBarnRaiseWell()) { // Scale down bean supply by the locked beans, if there is fertilizer to be paid off. // Note: This statement is put into the for loop to prevent another extraneous read of // the twaReserves from storage as `twaReserves` are already loaded into memory. if (LibAppStorage.diamondStorage().season.fertilizing == true) { beanSupply = beanSupply.sub(LibUnripe.getLockedBeans(twaReserves)); } } // If a new non-Well LP is added, functionality to calculate the USD value of the // liquidity should be added here. } // if there is no liquidity, // return 0 to save gas. if (totalUsdLiquidity == 0) return (Decimal.zero(), address(0), true); // USD liquidity is scaled down from 1e18 to match Bean precision (1e6). lpToSupplyRatio = Decimal.ratio(totalUsdLiquidity.div(LIQUIDITY_PRECISION), beanSupply); } /** * @notice Get the deltaPodDemand, lpToSupplyRatio, and podRate, and update soil demand * parameters. */ function getBeanstalkState(uint256 beanSupply) internal returns (BeanstalkState memory state) { AppStorage storage s = LibAppStorage.diamondStorage(); // Calculate Delta Soil Demand uint256 dsoil = s.f.beanSown; s.f.beanSown = 0; (state.deltaPodDemand, s.w.lastSowTime, s.w.thisSowTime) = calcDeltaPodDemand(dsoil); s.w.lastDSoil = uint128(dsoil); // SafeCast not necessary as `s.f.beanSown` is uint128. // Calculate Lp To Supply Ratio, fetching the twaReserves in storage: (state.lpToSupplyRatio, state.largestLiqWell, state.oracleFailure) = calcLPToSupplyRatio(beanSupply); // Calculate PodRate state.podRate = Decimal.ratio(s.f.pods.sub(s.f.harvestable), beanSupply); // Pod Rate } /** * @notice Evaluates beanstalk based on deltaB, podRate, deltaPodDemand and lpToSupplyRatio. * and returns the associated caseId. */ function evaluateBeanstalk( int256 deltaB, uint256 beanSupply ) internal returns (uint256, address, bool) { BeanstalkState memory bs = getBeanstalkState(beanSupply); uint256 caseId = evalPodRate(bs.podRate) // Evaluate Pod Rate .add(evalPrice(deltaB, bs.largestLiqWell)) // Evaluate Price .add(evalDeltaPodDemand(bs.deltaPodDemand)) // Evaluate Delta Soil Demand .add(evalLpToSupplyRatio(bs.lpToSupplyRatio)); // Evaluate LP to Supply Ratio return (caseId, bs.largestLiqWell, bs.oracleFailure); } /** * @notice calculates the liquidity weight of a token. * @dev the liquidity weight determines the percentage of * liquidity is considered in evaluating the liquidity of bean. * At 0, no liquidity is added. at 1e18, all liquidity is added. * * if failure, returns 0 (no liquidity is considered) instead of reverting. */ function getLiquidityWeight( bytes4 lwSelector ) internal view returns (uint256 liquidityWeight) { bytes memory callData = abi.encodeWithSelector(lwSelector); (bool success, bytes memory data) = address(this).staticcall(callData); if (!success) { return 0; } assembly { liquidityWeight := mload(add(data, add(0x20, 0))) } } }
/* SPDX-License-Identifier: MIT */ pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/SafeCast.sol"; import {AppStorage, LibAppStorage} from "./LibAppStorage.sol"; import {LibSafeMath128} from "./LibSafeMath128.sol"; import {C} from "../C.sol"; import {LibUnripe} from "./LibUnripe.sol"; import {IWell} from "contracts/interfaces/basin/IWell.sol"; import {LibBarnRaise} from "./LibBarnRaise.sol"; import {LibDiamond} from "contracts/libraries/LibDiamond.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import {LibWell} from "contracts/libraries/Well/LibWell.sol"; import {LibUsdOracle} from "contracts/libraries/Oracle/LibUsdOracle.sol"; /** * @author Publius * @title Fertilizer **/ library LibFertilizer { using SafeMath for uint256; using LibSafeMath128 for uint128; using SafeCast for uint256; using SafeERC20 for IERC20; using LibWell for address; event SetFertilizer(uint128 id, uint128 bpf); // 6 - 3 uint128 private constant PADDING = 1e3; uint128 private constant DECIMALS = 1e6; uint128 private constant REPLANT_SEASON = 6074; uint128 private constant RESTART_HUMIDITY = 2500; uint128 private constant END_DECREASE_SEASON = REPLANT_SEASON + 461; function addFertilizer( uint128 season, uint256 tokenAmountIn, uint256 fertilizerAmount, uint256 minLP ) internal returns (uint128 id) { AppStorage storage s = LibAppStorage.diamondStorage(); uint128 fertilizerAmount128 = fertilizerAmount.toUint128(); // Calculate Beans Per Fertilizer and add to total owed uint128 bpf = getBpf(season); s.unfertilizedIndex = s.unfertilizedIndex.add( fertilizerAmount.mul(bpf) ); // Get id id = s.bpf.add(bpf); // Update Total and Season supply s.fertilizer[id] = s.fertilizer[id].add(fertilizerAmount128); s.activeFertilizer = s.activeFertilizer.add(fertilizerAmount); // Add underlying to Unripe Beans and Unripe LP addUnderlying(tokenAmountIn, fertilizerAmount.mul(DECIMALS), minLP); // If not first time adding Fertilizer with this id, return if (s.fertilizer[id] > fertilizerAmount128) return id; // If first time, log end Beans Per Fertilizer and add to Season queue. push(id); emit SetFertilizer(id, bpf); } function getBpf(uint128 id) internal pure returns (uint128 bpf) { bpf = getHumidity(id).add(1000).mul(PADDING); } function getHumidity(uint128 id) internal pure returns (uint128 humidity) { if (id == 0) return 5000; if (id >= END_DECREASE_SEASON) return 200; uint128 humidityDecrease = id.sub(REPLANT_SEASON).mul(5); humidity = RESTART_HUMIDITY.sub(humidityDecrease); } /** * @dev Any token contributions should already be transferred to the Barn Raise Well to allow for a gas efficient liquidity * addition through the use of `sync`. See {FertilizerFacet.mintFertilizer} for an example. */ function addUnderlying(uint256 tokenAmountIn, uint256 usdAmount, uint256 minAmountOut) internal { AppStorage storage s = LibAppStorage.diamondStorage(); // Calculate how many new Deposited Beans will be minted uint256 percentToFill = usdAmount.mul(C.precision()).div( remainingRecapitalization() ); uint256 newDepositedBeans; if (C.unripeBean().totalSupply() > s.u[C.UNRIPE_BEAN].balanceOfUnderlying) { newDepositedBeans = (C.unripeBean().totalSupply()).sub( s.u[C.UNRIPE_BEAN].balanceOfUnderlying ); newDepositedBeans = newDepositedBeans.mul(percentToFill).div( C.precision() ); } // Calculate how many Beans to add as LP uint256 newDepositedLPBeans = usdAmount.mul(C.exploitAddLPRatio()).div( DECIMALS ); // Mint the Deposited Beans to Beanstalk. C.bean().mint( address(this), newDepositedBeans ); // Mint the LP Beans and add liquidity to the well. address barnRaiseWell = LibBarnRaise.getBarnRaiseWell(); address barnRaiseToken = LibBarnRaise.getBarnRaiseToken(); C.bean().mint( address(this), newDepositedLPBeans ); IERC20(barnRaiseToken).transferFrom( msg.sender, address(this), uint256(tokenAmountIn) ); IERC20(barnRaiseToken).approve(barnRaiseWell, uint256(tokenAmountIn)); C.bean().approve(barnRaiseWell, newDepositedLPBeans); uint256[] memory tokenAmountsIn = new uint256[](2); IERC20[] memory tokens = IWell(barnRaiseWell).tokens(); (tokenAmountsIn[0], tokenAmountsIn[1]) = tokens[0] == C.bean() ? (newDepositedLPBeans, tokenAmountIn) : (tokenAmountIn, newDepositedLPBeans); uint256 newLP = IWell(barnRaiseWell).addLiquidity( tokenAmountsIn, minAmountOut, address(this), type(uint256).max ); // Increment underlying balances of Unripe Tokens LibUnripe.incrementUnderlying(C.UNRIPE_BEAN, newDepositedBeans); LibUnripe.incrementUnderlying(C.UNRIPE_LP, newLP); s.recapitalized = s.recapitalized.add(usdAmount); } function push(uint128 id) internal { AppStorage storage s = LibAppStorage.diamondStorage(); if (s.fFirst == 0) { // Queue is empty s.season.fertilizing = true; s.fLast = id; s.fFirst = id; } else if (id <= s.fFirst) { // Add to front of queue setNext(id, s.fFirst); s.fFirst = id; } else if (id >= s.fLast) { // Add to back of queue setNext(s.fLast, id); s.fLast = id; } else { // Add to middle of queue uint128 prev = s.fFirst; uint128 next = getNext(prev); // Search for proper place in line while (id > next) { prev = next; next = getNext(next); } setNext(prev, id); setNext(id, next); } } function remainingRecapitalization() internal view returns (uint256 remaining) { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 totalDollars = uint256(1e12).mul(C.unripeLP().totalSupply()).div(C.unripeLPPerDollar()).div(DECIMALS); totalDollars = totalDollars / 1e6 * 1e6; // round down to nearest USDC if (s.recapitalized >= totalDollars) return 0; return totalDollars.sub(s.recapitalized); } function pop() internal returns (bool) { AppStorage storage s = LibAppStorage.diamondStorage(); uint128 first = s.fFirst; s.activeFertilizer = s.activeFertilizer.sub(getAmount(first)); uint128 next = getNext(first); if (next == 0) { // If all Unfertilized Beans have been fertilized, delete line. require(s.activeFertilizer == 0, "Still active fertilizer"); s.fFirst = 0; s.fLast = 0; s.season.fertilizing = false; return false; } s.fFirst = getNext(first); return true; } function getAmount(uint128 id) internal view returns (uint256) { AppStorage storage s = LibAppStorage.diamondStorage(); return s.fertilizer[id]; } function getNext(uint128 id) internal view returns (uint128) { AppStorage storage s = LibAppStorage.diamondStorage(); return s.nextFid[id]; } function setNext(uint128 id, uint128 next) internal { AppStorage storage s = LibAppStorage.diamondStorage(); s.nextFid[id] = next; } function beginBarnRaiseMigration(address well) internal { AppStorage storage s = LibAppStorage.diamondStorage(); require(well.isWell(), "Fertilizer: Not a Whitelisted Well."); // The Barn Raise only supports 2 token Wells where 1 token is Bean and the // other is supported by the Lib Usd Oracle. IERC20[] memory tokens = IWell(well).tokens(); require(tokens.length == 2, "Fertilizer: Well must have 2 tokens."); require( tokens[0] == C.bean() || tokens[1] == C.bean(), "Fertilizer: Well must have BEAN." ); // Check that Lib Usd Oracle supports the non-Bean token in the Well. LibUsdOracle.getTokenPrice(address(tokens[tokens[0] == C.bean() ? 1 : 0])); uint256 balanceOfUnderlying = s.u[C.UNRIPE_LP].balanceOfUnderlying; IERC20(s.u[C.UNRIPE_LP].underlyingToken).safeTransfer( LibDiamond.diamondStorage().contractOwner, balanceOfUnderlying ); LibUnripe.decrementUnderlying(C.UNRIPE_LP, balanceOfUnderlying); LibUnripe.switchUnderlyingToken(C.UNRIPE_LP, well); } }
/* SPDX-License-Identifier: MIT */ pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {LibAppStorage, AppStorage, Storage} from "./LibAppStorage.sol"; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/SafeCast.sol"; import {LibWhitelistedTokens} from "contracts/libraries/Silo/LibWhitelistedTokens.sol"; import {LibWhitelist} from "contracts/libraries/Silo/LibWhitelist.sol"; import {LibSafeMath32} from "contracts/libraries/LibSafeMath32.sol"; import {C} from "../C.sol"; import {LibWell} from "contracts/libraries/Well/LibWell.sol"; /** * @title LibGauge * @author Brean, Brendan * @notice LibGauge handles functionality related to the seed gauge system. */ library LibGauge { using SafeCast for uint256; using SafeMath for uint256; using LibSafeMath32 for uint32; uint256 internal constant BDV_PRECISION = 1e6; uint256 internal constant GP_PRECISION = 1e18; // Max and min are the ranges that the beanToMaxLpGpPerBdvRatioScaled can output. uint256 internal constant MAX_BEAN_MAX_LP_GP_PER_BDV_RATIO = 100e18; uint256 internal constant MIN_BEAN_MAX_LP_GP_PER_BDV_RATIO = 50e18; uint256 internal constant BEAN_MAX_LP_GP_RATIO_RANGE = MAX_BEAN_MAX_LP_GP_PER_BDV_RATIO - MIN_BEAN_MAX_LP_GP_PER_BDV_RATIO; // The maximum value of beanToMaxLpGpPerBdvRatio. uint256 internal constant ONE_HUNDRED_PERCENT = 100e18; // 24 * 30 * 6 uint256 internal constant TARGET_SEASONS_TO_CATCHUP = 4320; uint256 internal constant STALK_BDV_PRECISION = 1e4; /** * @notice Emitted when the AverageGrownStalkPerBdvPerSeason Updates. */ event UpdateAverageStalkPerBdvPerSeason(uint256 newStalkPerBdvPerSeason); struct LpGaugePointData { address lpToken; uint256 gpPerBdv; } /** * @notice Emitted when the gaugePoints for an LP silo token changes. * @param season The current Season * @param token The LP silo token whose gaugePoints was updated. * @param gaugePoints The new gaugePoints for the LP silo token. */ event GaugePointChange(uint256 indexed season, address indexed token, uint256 gaugePoints); /** * @notice Updates the seed gauge system. * @dev Updates the GaugePoints for LP assets (if applicable) * and the distribution of grown Stalk to silo assets. * * If any of the LP price oracle failed, * then the gauge system should be skipped, as a valid * usd liquidity value cannot be computed. */ function stepGauge() external { ( uint256 maxLpGpPerBdv, LpGaugePointData[] memory lpGpData, uint256 totalGaugePoints, uint256 totalLpBdv ) = updateGaugePoints(); // If totalLpBdv is max, it means that the gauge points has failed, // and the gauge system should be skipped. if (totalLpBdv == type(uint256).max) return; updateGrownStalkEarnedPerSeason(maxLpGpPerBdv, lpGpData, totalGaugePoints, totalLpBdv); } /** * @notice Evaluate the gauge points of each LP asset. * @dev `totalLpBdv` is returned as type(uint256).max when an Oracle failure occurs. */ function updateGaugePoints() internal returns ( uint256 maxLpGpPerBdv, LpGaugePointData[] memory lpGpData, uint256 totalGaugePoints, uint256 totalLpBdv ) { AppStorage storage s = LibAppStorage.diamondStorage(); address[] memory whitelistedLpTokens = LibWhitelistedTokens.getWhitelistedLpTokens(); lpGpData = new LpGaugePointData[](whitelistedLpTokens.length); // If there is only one pool, there is no need to update the gauge points. if (whitelistedLpTokens.length == 1) { // If the usd price oracle failed, skip gauge point update. // Assumes that only Wells use USD price oracles. if ( LibWell.isWell(whitelistedLpTokens[0]) && s.usdTokenPrice[whitelistedLpTokens[0]] == 0 ) { return (maxLpGpPerBdv, lpGpData, totalGaugePoints, type(uint256).max); } uint256 gaugePoints = s.ss[whitelistedLpTokens[0]].gaugePoints; lpGpData[0].lpToken = whitelistedLpTokens[0]; // If nothing has been deposited, skip gauge point update. uint128 depositedBdv = s.siloBalances[whitelistedLpTokens[0]].depositedBdv; if (depositedBdv == 0) return (maxLpGpPerBdv, lpGpData, totalGaugePoints, type(uint256).max); lpGpData[0].gpPerBdv = gaugePoints.mul(BDV_PRECISION).div( s.siloBalances[whitelistedLpTokens[0]].depositedBdv ); return ( lpGpData[0].gpPerBdv, lpGpData, gaugePoints, s.siloBalances[whitelistedLpTokens[0]].depositedBdv ); } // Summate total deposited BDV across all whitelisted LP tokens. for (uint256 i; i < whitelistedLpTokens.length; ++i) { // Assumes that only Wells use USD price oracles. if ( LibWell.isWell(whitelistedLpTokens[i]) && s.usdTokenPrice[whitelistedLpTokens[i]] == 0 ) { return (maxLpGpPerBdv, lpGpData, totalGaugePoints, type(uint256).max); } totalLpBdv = totalLpBdv.add(s.siloBalances[whitelistedLpTokens[i]].depositedBdv); } // If nothing has been deposited, skip gauge point update. if (totalLpBdv == 0) return (maxLpGpPerBdv, lpGpData, totalGaugePoints, type(uint256).max); // Calculate and update the gauge points for each LP. for (uint256 i; i < whitelistedLpTokens.length; ++i) { Storage.SiloSettings storage ss = s.ss[whitelistedLpTokens[i]]; uint256 depositedBdv = s.siloBalances[whitelistedLpTokens[i]].depositedBdv; // 1e6 = 1% uint256 percentDepositedBdv = depositedBdv.mul(100e6).div(totalLpBdv); // Calculate the new gauge points of the token. uint256 newGaugePoints = calcGaugePoints( ss.gpSelector, ss.gaugePoints, ss.optimalPercentDepositedBdv, percentDepositedBdv ); // Increment totalGaugePoints and calculate the gaugePoints per BDV: totalGaugePoints = totalGaugePoints.add(newGaugePoints); LpGaugePointData memory _lpGpData; _lpGpData.lpToken = whitelistedLpTokens[i]; // Gauge points has 18 decimal precision (GP_PRECISION = 1%) // Deposited BDV has 6 decimal precision (1e6 = 1 unit of BDV) uint256 gpPerBdv = depositedBdv > 0 ? newGaugePoints.mul(BDV_PRECISION).div(depositedBdv) : 0; // gpPerBdv has 18 decimal precision. if (gpPerBdv > maxLpGpPerBdv) maxLpGpPerBdv = gpPerBdv; _lpGpData.gpPerBdv = gpPerBdv; lpGpData[i] = _lpGpData; ss.gaugePoints = newGaugePoints.toUint128(); emit GaugePointChange(s.season.current, whitelistedLpTokens[i], ss.gaugePoints); } } /** * @notice Calculates the new gauge points for the given token. * @dev Function calls the selector of the token's gauge point function. * Currently all assets uses the default GaugePoint Function. * {GaugePointFacet.defaultGaugePointFunction()} */ function calcGaugePoints( bytes4 gpSelector, uint256 gaugePoints, uint256 optimalPercentDepositedBdv, uint256 percentDepositedBdv ) internal view returns (uint256 newGaugePoints) { bytes memory callData = abi.encodeWithSelector( gpSelector, gaugePoints, optimalPercentDepositedBdv, percentDepositedBdv ); (bool success, bytes memory data) = address(this).staticcall(callData); if (!success) { if (data.length == 0) revert(); assembly { revert(add(32, data), mload(data)) } } assembly { newGaugePoints := mload(add(data, add(0x20, 0))) } } /** * @notice Updates the average grown stalk per BDV per Season for whitelisted Beanstalk assets. * @dev Called at the end of each Season. * The gauge system considers the total BDV of all whitelisted silo tokens, excluding unripe assets. */ function updateGrownStalkEarnedPerSeason( uint256 maxLpGpPerBdv, LpGaugePointData[] memory lpGpData, uint256 totalGaugePoints, uint256 totalLpBdv ) internal { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 beanDepositedBdv = s.siloBalances[C.BEAN].depositedBdv; uint256 totalGaugeBdv = totalLpBdv.add(beanDepositedBdv); // If nothing has been deposited, skip grown stalk update. if (totalGaugeBdv == 0) return; // Calculate the ratio between the bean and the max LP gauge points per BDV. // 18 decimal precision. uint256 beanToMaxLpGpPerBdvRatio = getBeanToMaxLpGpPerBdvRatioScaled( s.seedGauge.beanToMaxLpGpPerBdvRatio ); // Get the GaugePoints and GPperBDV for bean // BeanGpPerBdv and beanToMaxLpGpPerBdvRatio has 18 decimal precision. uint256 beanGpPerBdv = maxLpGpPerBdv.mul(beanToMaxLpGpPerBdvRatio).div(100e18); totalGaugePoints = totalGaugePoints.add( beanGpPerBdv.mul(beanDepositedBdv).div(BDV_PRECISION) ); // update the average grown stalk per BDV per Season. // beanstalk must exist for a minimum of the catchup season in order to update the average. if (s.season.current > TARGET_SEASONS_TO_CATCHUP) { updateAverageStalkPerBdvPerSeason(); } // Calculate grown stalk issued this season and GrownStalk Per GaugePoint. uint256 newGrownStalk = uint256(s.seedGauge.averageGrownStalkPerBdvPerSeason) .mul(totalGaugeBdv) .div(BDV_PRECISION); // Gauge points has 18 decimal precision. uint256 newGrownStalkPerGp = newGrownStalk.mul(GP_PRECISION).div(totalGaugePoints); // Update stalkPerBdvPerSeason for bean. issueGrownStalkPerBdv(C.BEAN, newGrownStalkPerGp, beanGpPerBdv); // Update stalkPerBdvPerSeason for LP // If there is only one pool, then no need to read gauge points. if (lpGpData.length == 1) { issueGrownStalkPerBdv(lpGpData[0].lpToken, newGrownStalkPerGp, lpGpData[0].gpPerBdv); } else { for (uint256 i; i < lpGpData.length; i++) { issueGrownStalkPerBdv( lpGpData[i].lpToken, newGrownStalkPerGp, lpGpData[i].gpPerBdv ); } } } /** * @notice issues the grown stalk per BDV for the given token. * @param token the token to issue the grown stalk for. * @param grownStalkPerGp the number of GrownStalk Per Gauge Point. * @param gpPerBdv the amount of GaugePoints per BDV the token has. */ function issueGrownStalkPerBdv( address token, uint256 grownStalkPerGp, uint256 gpPerBdv ) internal { LibWhitelist.updateStalkPerBdvPerSeasonForToken( token, grownStalkPerGp.mul(gpPerBdv).div(GP_PRECISION).toUint32() ); } /** * @notice Updates the UpdateAverageStalkPerBdvPerSeason in the seed gauge. * @dev The function updates the targetGrownStalkPerBdvPerSeason such that * it will take 6 months for the average new depositer to catch up to the current * average grown stalk per BDV. */ function updateAverageStalkPerBdvPerSeason() internal { AppStorage storage s = LibAppStorage.diamondStorage(); // Will overflow if the average grown stalk per BDV exceeds 1.4e36, // which is highly improbable assuming consistent new deposits. // Thus, safeCast was determined is to be unnecessary. s.seedGauge.averageGrownStalkPerBdvPerSeason = uint128( getAverageGrownStalkPerBdv().mul(BDV_PRECISION).div(TARGET_SEASONS_TO_CATCHUP) ); emit UpdateAverageStalkPerBdvPerSeason(s.seedGauge.averageGrownStalkPerBdvPerSeason); } /** * @notice Returns the total BDV in beanstalk. * @dev The total BDV may differ from the instaneous BDV, * as BDV is asyncronous. * Note We get the silo Tokens, not the whitelisted tokens * to account for grown stalk from dewhitelisted tokens. */ function getTotalBdv() internal view returns (uint256 totalBdv) { AppStorage storage s = LibAppStorage.diamondStorage(); address[] memory siloTokens = LibWhitelistedTokens.getSiloTokens(); for (uint256 i; i < siloTokens.length; ++i) { totalBdv = totalBdv.add(s.siloBalances[siloTokens[i]].depositedBdv); } } /** * @notice Returns the average grown stalk per BDV. * @dev `totalBDV` refers to the total BDV deposited in the silo. */ function getAverageGrownStalkPerBdv() internal view returns (uint256) { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 totalBdv = getTotalBdv(); if (totalBdv == 0) return 0; return s.s.stalk.div(totalBdv).sub(STALK_BDV_PRECISION); } /** * @notice Returns the ratio between the bean and * the max LP gauge points per BDV. * @dev s.seedGauge.beanToMaxLpGpPerBdvRatio is a number between 0 and 100e18, * where f(0) = MIN_BEAN_MAX_LPGP_RATIO and f(100e18) = MAX_BEAN_MAX_LPGP_RATIO. * At the minimum value (0), beans should have half of the * largest gauge points per BDV out of the LPs. * At the maximum value (100e18), beans should have the same amount of * gauge points per BDV as the largest out of the LPs. */ function getBeanToMaxLpGpPerBdvRatioScaled( uint256 beanToMaxLpGpPerBdvRatio ) internal pure returns (uint256) { return beanToMaxLpGpPerBdvRatio.mul(BEAN_MAX_LP_GP_RATIO_RANGE).div(ONE_HUNDRED_PERCENT).add( MIN_BEAN_MAX_LP_GP_PER_BDV_RATIO ); } }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {IBlockBasefee} from "../interfaces/IBlockBasefee.sol"; import "@openzeppelin/contracts/math/Math.sol"; import "../C.sol"; /** * @title LibIncentive * @author Publius, Chaikitty, Brean * @notice Calculates the reward offered for calling Sunrise, adjusts for current gas & ETH prices, * and scales the reward up when the Sunrise is called late. */ library LibIncentive { using SafeMath for uint256; /** * @notice Emitted when Beanstalk pays `beans` to `account` as a reward for calling `sunrise()`. * @param account The address to which the reward Beans were sent * @param beans The amount of Beans paid as a reward */ event Incentivization(address indexed account, uint256 beans); /// @dev The time range over which to consult the Uniswap V3 ETH:USDC pool oracle. Measured in seconds. uint32 internal constant PERIOD = 1800; // 30 minutes /// @dev The Sunrise reward reaches its maximum after this many blocks elapse. uint256 internal constant MAX_BLOCKS_LATE = 25; /// @dev Base BEAN reward to cover cost of operating a bot. uint256 internal constant BASE_REWARD = 3e6; // 3 BEAN /// @dev Max BEAN reward for calling Sunrise. uint256 internal constant MAX_REWARD = 250e6; // 250 BEAN /// @dev Wei buffer to account for the priority fee. uint256 internal constant PRIORITY_FEE_BUFFER = 5e9; // 5e9 wei = 5 gwei /// @dev The maximum gas which Beanstalk will pay for a Sunrise transaction. uint256 internal constant MAX_SUNRISE_GAS = 500_000; // 500k gas /// @dev Accounts for extra gas overhead for completing a Sunrise tranasaction. // 21k gas (base cost for a transction) + ~29 gas for other overhead uint256 internal constant SUNRISE_GAS_OVERHEAD = 50_000; // 50k gas /// @dev Use external contract for block.basefee as to avoid upgrading existing contracts to solidity v8 address private constant BASE_FEE_CONTRACT = 0x84292919cB64b590C0131550483707E43Ef223aC; /// @dev `sunriseReward` is precomputed in {fracExp} using this precision. uint256 private constant FRAC_EXP_PRECISION = 1e18; //////////////////// CALCULATE REWARD //////////////////// /** * @param initialGasLeft The amount of gas left at the start of the transaction * @param blocksLate The number of blocks late that {sunrise()} was called. * @param beanEthPrice The Bean:Eth price calculated by the Minting Well. The amount of Beans per ETH. * @dev Calculates Sunrise incentive amount based on current gas prices and a computed * BEAN:ETH price. This function is called at the end of {sunriseTo()} after all * "step" functions have been executed. */ function determineReward(uint256 initialGasLeft, uint256 blocksLate, uint256 beanEthPrice) external view returns (uint256) { // Cap the maximum number of blocks late. If the sunrise is later than // this, Beanstalk will pay the same amount. Prevents unbounded return value. if (blocksLate > MAX_BLOCKS_LATE) { blocksLate = MAX_BLOCKS_LATE; } // If the Bean Eth pool couldn't calculate a valid price, use the max reward value. if (beanEthPrice <= 1) { return fracExp(MAX_REWARD, blocksLate); } // Sunrise gas overhead includes: // - 21K for base transaction cost // - 29K for calculations following the below line, like {fracExp} // Max gas which Beanstalk will pay for = 500K. uint256 gasUsed = Math.min(initialGasLeft.sub(gasleft()) + SUNRISE_GAS_OVERHEAD, MAX_SUNRISE_GAS); // Calculate the current cost in Wei of `gasUsed` gas. // {block_basefee()} returns the base fee of the current block in Wei. // Adds a buffer for priority fee. uint256 gasCostWei = IBlockBasefee(BASE_FEE_CONTRACT).block_basefee().add(PRIORITY_FEE_BUFFER).mul(gasUsed); // (BASE_FEE // + PRIORITY_FEE_BUFFER) // * GAS_USED // Calculates the Sunrise reward to pay in BEAN. uint256 sunriseReward = Math.min( BASE_REWARD + gasCostWei.mul(beanEthPrice).div(1e18), // divide by 1e18 to convert wei to eth MAX_REWARD ); // Scale the reward up as the number of blocks after expected sunrise increases. // `sunriseReward * (1 + 1/100)^(blocks late * seconds per block)` // NOTE: 1.01^(25 * 12) = 19.78, This is the maximum multiplier. return fracExp(sunriseReward, blocksLate); } //////////////////// MATH UTILITIES //////////////////// /** * @dev fraxExp scales up the bean reward based on the blocks late. * the formula is beans * (1.01)^(Blocks Late * 12 second block time). * since block time is capped at 25 blocks, * we only need to check cases 0 - 25 */ function fracExp(uint256 beans, uint256 blocksLate) internal pure returns (uint256 scaledSunriseReward) { // check most likely case first if (blocksLate == 0) { return beans; } // Binary Search if (blocksLate < 13) { if (blocksLate < 7) { if (blocksLate < 4) { if (blocksLate < 2) { // blocksLate == 0 is already checked, thus // blocksLate = 1, 1.01^(1*12) return _scaleReward(beans, 1_126_825_030_131_969_720); } if (blocksLate == 2) { // 1.01^(2*12) return _scaleReward(beans, 1_269_734_648_531_914_468); } else { // blocksLate == 3, 1.01^(3*12) return _scaleReward(beans, 1_430_768_783_591_580_504); } } if (blocksLate < 6) { if (blocksLate == 4) { return _scaleReward(beans, 1_612_226_077_682_464_366); } else { // blocksLate == 5 return _scaleReward(beans, 1_816_696_698_564_090_264); } } else { // blocksLate == 6 return _scaleReward(beans, 2_047_099_312_100_130_925); } } if (blocksLate < 10) { if (blocksLate < 9) { if (blocksLate == 7) { return _scaleReward(beans, 2_306_722_744_040_364_517); } else { // blocksLate == 8 return _scaleReward(beans, 2_599_272_925_559_383_624); } } else { // blocksLate == 9 return _scaleReward(beans, 2_928_925_792_664_665_541); } } if (blocksLate < 12) { if (blocksLate == 10) { return _scaleReward(beans, 3_300_386_894_573_665_047); } else { // blocksLate == 11 return _scaleReward(beans, 3_718_958_561_925_128_091); } } else { // blocksLate == 12 return _scaleReward(beans, 4_190_615_593_600_829_241); } } if (blocksLate < 19) { if (blocksLate < 16) { if (blocksLate < 15) { if (blocksLate == 13) { return _scaleReward(beans, 4_722_090_542_530_756_587); } else { // blocksLate == 14 return _scaleReward(beans, 5_320_969_817_873_109_037); } } else { // blocksLate == 15 return _scaleReward(beans, 5_995_801_975_356_167_528); } } if (blocksLate < 18) { if (blocksLate == 16) { return _scaleReward(beans, 6_756_219_741_546_037_047); } else { // blocksLate == 17 return _scaleReward(beans, 7_613_077_513_845_821_874); } } return _scaleReward(beans, 8_578_606_298_936_339_361); // blocksLate == 18 } if (blocksLate < 22) { if (blocksLate < 21) { if (blocksLate == 19) { return _scaleReward(beans, 9_666_588_301_289_245_846); } else { // blocksLate == 20 return _scaleReward(beans, 10_892_553_653_873_600_447); } } return _scaleReward(beans, 12_274_002_099_240_216_703); // blocksLate == 21 } if (blocksLate <= 23) { if (blocksLate == 22) { return _scaleReward(beans, 13_830_652_785_316_216_792); } else { // blocksLate == 23 return _scaleReward(beans, 15_584_725_741_558_756_931); } } if (blocksLate >= 25) { // block rewards are capped at 25 (MAX_BLOCKS_LATE) return _scaleReward(beans, 19_788_466_261_924_388_319); } else { // blocksLate == 24 return _scaleReward(beans, 17_561_259_053_330_430_428); } } function _scaleReward(uint256 beans, uint256 scaler) private pure returns (uint256) { return beans.mul(scaler).div(FRAC_EXP_PRECISION); } }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {AppStorage, LibAppStorage} from "./LibAppStorage.sol"; /** * @title LibLockedUnderlying * @author Brendan * @notice Library to calculate the number of Underlying Tokens that would be locked if all of * the Unripe Tokens are Chopped. */ library LibLockedUnderlying { using SafeMath for uint256; uint256 constant DECIMALS = 1e6; /** * @notice Return the amount of Underlying Tokens that would be locked if all of the Unripe Tokens * were chopped. */ function getLockedUnderlying( address unripeToken, uint256 recapPercentPaid ) external view returns (uint256 lockedUnderlying) { AppStorage storage s = LibAppStorage.diamondStorage(); return s .u[unripeToken] .balanceOfUnderlying .mul(getPercentLockedUnderlying(unripeToken, recapPercentPaid)) .div(1e18); } /** * @notice Return the % of Underlying Tokens that would be locked if all of the Unripe Tokens * were chopped. * @param unripeToken The address of the Unripe Token * @param recapPercentPaid The % of Sprouts that have been Rinsed or are Rinsable. * Should have 6 decimal precision. * * @dev Solves the below equation for N_{⌈U/i⌉}: * N_{t+1} = N_t - i * R * N_t / (U - i * t) * where: * - N_t is the number of Underlying Tokens at step t * - U is the starting number of Unripe Tokens * - R is the % of Sprouts that are Rinsable or Rinsed * - i is the number of Unripe Beans that are chopped at each step. i ~= 46,659 is used as this is aboutr * the average Unripe Beans held per Farmer with a non-zero balance. * * The equation is solved by using a lookup table of N_{⌈U/i⌉} values for different values of * U and R (The solution is independent of N) as solving iteratively is too computationally * expensive and there is no more efficient way to solve the equation. * * The lookup threshold assumes no decimal precision. This library only supports * unripe tokens with 6 decimals. */ function getPercentLockedUnderlying( address unripeToken, uint256 recapPercentPaid ) private view returns (uint256 percentLockedUnderlying) { uint256 unripeSupply = IERC20(unripeToken).totalSupply().div(DECIMALS); if (unripeSupply < 1_000_000) return 0; // If < 1,000,000 Assume all supply is unlocked. if (unripeSupply > 5_000_000) { if (unripeSupply > 10_000_000) { if (recapPercentPaid > 0.1e6) { if (recapPercentPaid > 0.21e6) { if (recapPercentPaid > 0.38e6) { if (recapPercentPaid > 0.45e6) { return 0.000106800755371506e18; // 90,000,000, 0.9 } else { return 0.019890729697455534e18; // 90,000,000, 0.45 } } else if (recapPercentPaid > 0.29e6) { if (recapPercentPaid > 0.33e6) { return 0.038002726385307994e18; // 90,000,000 0.38 } else { return 0.05969915165233464e18; // 90,000,000 0.33 } } else if (recapPercentPaid > 0.25e6) { if (recapPercentPaid > 0.27e6) { return 0.08520038853809475e18; // 90,000,000 0.29 } else { return 0.10160827712172482e18; // 90,000,000 0.27 } } else { if (recapPercentPaid > 0.23e6) { return 0.1210446758987509e18; // 90,000,000 0.25 } else { return 0.14404919400935834e18; // 90,000,000 0.23 } } } else { if (recapPercentPaid > 0.17e6) { if (recapPercentPaid > 0.19e6) { return 0.17125472579906187e18; // 90,000,000, 0.21 } else { return 0.2034031571094802e18; // 90,000,000, 0.19 } } else if (recapPercentPaid > 0.14e6) { if (recapPercentPaid > 0.15e6) { return 0.24136365460186238e18; // 90,000,000 0.17 } else { return 0.2861539540121635e18; // 90,000,000 0.15 } } else if (recapPercentPaid > 0.12e6) { if (recapPercentPaid > 0.13e6) { return 0.3114749615435798e18; // 90,000,000 0.14 } else { return 0.3389651289211062e18; // 90,000,000 0.13 } } else { if (recapPercentPaid > 0.11e6) { return 0.3688051484970447e18; // 90,000,000 0.12 } else { return 0.4011903974987394e18; // 90,000,000 0.11 } } } } else { if (recapPercentPaid > 0.04e6) { if (recapPercentPaid > 0.08e6) { if (recapPercentPaid > 0.09e6) { return 0.4363321054081788e18; // 90,000,000, 0.1 } else { return 0.4744586123058411e18; // 90,000,000, 0.09 } } else if (recapPercentPaid > 0.06e6) { if (recapPercentPaid > 0.07e6) { return 0.5158167251384363e18; // 90,000,000 0.08 } else { return 0.560673179393784e18; // 90,000,000 0.07 } } else if (recapPercentPaid > 0.05e6) { if (recapPercentPaid > 0.055e6) { return 0.6093162142284054e18; // 90,000,000 0.06 } else { return 0.6351540690346162e18; // 90,000,000 0.055 } } else { if (recapPercentPaid > 0.045e6) { return 0.6620572696973799e18; // 90,000,000 0.05 } else { return 0.6900686713435757e18; // 90,000,000 0.045 } } } else { if (recapPercentPaid > 0.03e6) { if (recapPercentPaid > 0.035e6) { return 0.7192328153846157e18; // 90,000,000, 0.04 } else { return 0.7495959945573412e18; // 90,000,000, 0.035 } } else if (recapPercentPaid > 0.02e6) { if (recapPercentPaid > 0.025e6) { return 0.7812063204281795e18; // 90,000,000 0.03 } else { return 0.8141137934523504e18; // 90,000,000 0.025 } } else if (recapPercentPaid > 0.01e6) { if (recapPercentPaid > 0.015e6) { return 0.8483703756831885e18; // 90,000,000 0.02 } else { return 0.8840300662301638e18; // 90,000,000 0.015 } } else { if (recapPercentPaid > 0.005e6) { return 0.921148979567821e18; // 90,000,000 0.01 } else { return 0.9597854268015467e18; // 90,000,000 0.005 } } } } } else { // > 5,000,000 if (recapPercentPaid > 0.1e6) { if (recapPercentPaid > 0.21e6) { if (recapPercentPaid > 0.38e6) { if (recapPercentPaid > 0.45e6) { return 0.000340444522821781e18; // 10,000,000, 0.9 } else { return 0.04023093970853808e18; // 10,000,000, 0.45 } } else if (recapPercentPaid > 0.29e6) { if (recapPercentPaid > 0.33e6) { return 0.06954881077191022e18; // 10,000,000 0.38 } else { return 0.10145116013499655e18; // 10,000,000 0.33 } } else if (recapPercentPaid > 0.25e6) { if (recapPercentPaid > 0.27e6) { return 0.13625887314323348e18; // 10,000,000 0.29 } else { return 0.15757224609763754e18; // 10,000,000 0.27 } } else { if (recapPercentPaid > 0.23e6) { return 0.18197183407669726e18; // 10,000,000 0.25 } else { return 0.20987581330872107e18; // 10,000,000 0.23 } } } else { if (recapPercentPaid > 0.17e6) { if (recapPercentPaid > 0.19e6) { return 0.24175584233885106e18; // 10,000,000, 0.21 } else { return 0.27814356260741413e18; // 10,000,000, 0.19 } } else if (recapPercentPaid > 0.14e6) { if (recapPercentPaid > 0.15e6) { return 0.3196378540296301e18; // 10,000,000 0.17 } else { return 0.36691292973511136e18; // 10,000,000 0.15 } } else if (recapPercentPaid > 0.1e6) { if (recapPercentPaid > 0.13e6) { return 0.3929517529835418e18; // 10,000,000 0.14 } else { return 0.4207273631610372e18; // 10,000,000 0.13 } } else { if (recapPercentPaid > 0.11e6) { return 0.450349413795883e18; // 10,000,000 0.12 } else { return 0.4819341506654745e18; // 10,000,000 0.11 } } } } else { if (recapPercentPaid > 0.04e6) { if (recapPercentPaid > 0.08e6) { if (recapPercentPaid > 0.09e6) { return 0.5156047910307769e18; // 10,000,000, 0.1 } else { return 0.551491923831086e18; // 10,000,000, 0.09 } } else if (recapPercentPaid > 0.06e6) { if (recapPercentPaid > 0.07e6) { return 0.5897339319558434e18; // 10,000,000 0.08 } else { return 0.6304774377677631e18; // 10,000,000 0.07 } } else if (recapPercentPaid > 0.05e6) { if (recapPercentPaid > 0.055e6) { return 0.6738777731119263e18; // 10,000,000 0.06 } else { return 0.6966252960203008e18; // 10,000,000 0.055 } } else { if (recapPercentPaid > 0.045e6) { return 0.7200994751088836e18; // 10,000,000 0.05 } else { return 0.7443224016328813e18; // 10,000,000 0.045 } } } else { if (recapPercentPaid > 0.03e6) { if (recapPercentPaid > 0.035e6) { return 0.7693168090963867e18; // 10,000,000, 0.04 } else { return 0.7951060911805916e18; // 10,000,000, 0.035 } } else if (recapPercentPaid > 0.02e6) { if (recapPercentPaid > 0.025e6) { return 0.8217143201541763e18; // 10,000,000 0.03 } else { return 0.8491662657783823e18; // 10,000,000 0.025 } } else if (recapPercentPaid > 0.01e6) { if (recapPercentPaid > 0.015e6) { return 0.8774874147196358e18; // 10,000,000 0.02 } else { return 0.9067039904828691e18; // 10,000,000 0.015 } } else { if (recapPercentPaid > 0.005e6) { return 0.9368429738790524e18; // 10,000,000 0.01 } else { return 0.9679321240407666e18; // 10,000,000 0.005 } } } } } } else { if (unripeSupply > 1_000_000) { if (recapPercentPaid > 0.1e6) { if (recapPercentPaid > 0.21e6) { if (recapPercentPaid > 0.38e6) { if (recapPercentPaid > 0.45e6) { return 0.000946395082480844e18; // 3,000,000, 0.9 } else { return 0.06786242725985348e18; // 3,000,000, 0.45 } } else if (recapPercentPaid > 0.29e6) { if (recapPercentPaid > 0.33e6) { return 0.10822315472628707e18; // 3,000,000 0.38 } else { return 0.14899524306327216e18; // 3,000,000 0.33 } } else if (recapPercentPaid > 0.25e6) { if (recapPercentPaid > 0.27e6) { return 0.1910488239684135e18; // 3,000,000 0.29 } else { return 0.215863137234529e18; // 3,000,000 0.27 } } else { if (recapPercentPaid > 0.23e6) { return 0.243564628757033e18; // 3,000,000 0.25 } else { return 0.2744582675491247e18; // 3,000,000 0.23 } } } else { if (recapPercentPaid > 0.17e6) { if (recapPercentPaid > 0.19e6) { return 0.3088786047254358e18; // 3,000,000, 0.21 } else { return 0.3471924328319608e18; // 3,000,000, 0.19 } } else if (recapPercentPaid > 0.14e6) { if (recapPercentPaid > 0.15e6) { return 0.38980166833777796e18; // 3,000,000 0.17 } else { return 0.4371464748698771e18; // 3,000,000 0.15 } } else if (recapPercentPaid > 0.12e6) { if (recapPercentPaid > 0.13e6) { return 0.46274355346663876e18; // 3,000,000 0.14 } else { return 0.4897086460787351e18; // 3,000,000 0.13 } } else { if (recapPercentPaid > 0.11e6) { return 0.518109082463349e18; // 3,000,000 0.12 } else { return 0.5480152684204499e18; // 3,000,000 0.11 } } } } else { if (recapPercentPaid > 0.04e6) { if (recapPercentPaid > 0.08e6) { if (recapPercentPaid > 0.09e6) { return 0.5795008171102514e18; // 3,000,000, 0.1 } else { return 0.6126426856374751e18; // 3,000,000, 0.09 } } else if (recapPercentPaid > 0.06e6) { if (recapPercentPaid > 0.07e6) { return 0.6475213171017626e18; // 3,000,000 0.08 } else { return 0.6842207883207123e18; // 3,000,000 0.07 } } else if (recapPercentPaid > 0.05e6) { if (recapPercentPaid > 0.055e6) { return 0.7228289634394097e18; // 3,000,000 0.06 } else { return 0.742877347280416e18; // 3,000,000 0.055 } } else { if (recapPercentPaid > 0.045e6) { return 0.7634376536479606e18; // 3,000,000 0.05 } else { return 0.784522002909275e18; // 3,000,000 0.045 } } } else { if (recapPercentPaid > 0.03e6) { if (recapPercentPaid > 0.035e6) { return 0.8061427832364296e18; // 3,000,000, 0.04 } else { return 0.8283126561589187e18; // 3,000,000, 0.035 } } else if (recapPercentPaid > 0.02e6) { if (recapPercentPaid > 0.025e6) { return 0.8510445622247672e18; // 3,000,000 0.03 } else { return 0.8743517267721741e18; // 3,000,000 0.025 } } else if (recapPercentPaid > 0.01e6) { if (recapPercentPaid > 0.015e6) { return 0.8982476658137254e18; // 3,000,000 0.02 } else { return 0.9227461920352636e18; // 3,000,000 0.015 } } else { if (recapPercentPaid > 0.005e6) { return 0.9478614209115208e18; // 3,000,000 0.01 } else { return 0.9736077769406731e18; // 3,000,000 0.005 } } } } } else { if (recapPercentPaid > 0.1e6) { if (recapPercentPaid > 0.21e6) { if (recapPercentPaid > 0.38e6) { if (recapPercentPaid > 0.45e6) { return 0.003360632002379016e18; // 1,000,000, 0.9 } else { return 0.12071031956650236e18; // 1,000,000, 0.45 } } else if (recapPercentPaid > 0.29e6) { if (recapPercentPaid > 0.33e6) { return 0.1752990554517151e18; // 1,000,000 0.38 } else { return 0.22598948369141458e18; // 1,000,000 0.33 } } else if (recapPercentPaid > 0.25e6) { if (recapPercentPaid > 0.27e6) { return 0.27509697387157794e18; // 1,000,000 0.29 } else { return 0.3029091410266461e18; // 1,000,000 0.27 } } else { if (recapPercentPaid > 0.23e6) { return 0.33311222196618273e18; // 1,000,000 0.25 } else { return 0.36588364748950297e18; // 1,000,000 0.23 } } } else { if (recapPercentPaid > 0.17e6) { if (recapPercentPaid > 0.19e6) { return 0.40141235983370593e18; // 1,000,000, 0.21 } else { return 0.43989947169522015e18; // 1,000,000, 0.19 } } else if (recapPercentPaid > 0.14e6) { if (recapPercentPaid > 0.15e6) { return 0.4815589587559236e18; // 1,000,000 0.17 } else { return 0.5266183872325827e18; // 1,000,000 0.15 } } else if (recapPercentPaid > 0.12e6) { if (recapPercentPaid > 0.13e6) { return 0.5504980973828455e18; // 1,000,000 0.14 } else { return 0.5753196780298556e18; // 1,000,000 0.13 } } else { if (recapPercentPaid > 0.11e6) { return 0.6011157438454372e18; // 1,000,000 0.12 } else { return 0.6279199091408495e18; // 1,000,000 0.11 } } } } else { if (recapPercentPaid > 0.04e6) { if (recapPercentPaid > 0.08e6) { if (recapPercentPaid > 0.09e6) { return 0.6557668151543954e18; // 1,000,000, 0.1 } else { return 0.6846921580052533e18; // 1,000,000, 0.09 } } else if (recapPercentPaid > 0.06e6) { if (recapPercentPaid > 0.07e6) { return 0.7147327173281093e18; // 1,000,000 0.08 } else { return 0.745926385603471e18; // 1,000,000 0.07 } } else if (recapPercentPaid > 0.05e6) { if (recapPercentPaid > 0.055e6) { return 0.7783121981988174e18; // 1,000,000 0.06 } else { return 0.7949646772335068e18; // 1,000,000 0.055 } } else { if (recapPercentPaid > 0.045e6) { return 0.8119303641360465e18; // 1,000,000 0.05 } else { return 0.8292144735871585e18; // 1,000,000 0.045 } } } else { if (recapPercentPaid > 0.03e6) { if (recapPercentPaid > 0.035e6) { return 0.8468222976009872e18; // 1,000,000, 0.04 } else { return 0.8647592065514869e18; // 1,000,000, 0.035 } } else if (recapPercentPaid > 0.02e6) { if (recapPercentPaid > 0.025e6) { return 0.8830306502110374e18; // 1,000,000 0.03 } else { return 0.9016421588014247e18; // 1,000,000 0.025 } } else if (recapPercentPaid > 0.01e6) { if (recapPercentPaid > 0.015e6) { return 0.9205993440573136e18; // 1,000,000 0.02 } else { return 0.9399079003023474e18; // 1,000,000 0.015 } } else { if (recapPercentPaid > 0.005e6) { return 0.959573605538012e18; // 1,000,000 0.01 } else { return 0.9796023225453983e18; // 1,000,000 0.005 } } } } } } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @author Publius * @title LibSafeMath128 is a uint128 variation of Open Zeppelin's Safe Math library. **/ library LibSafeMath128 { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint128 a, uint128 b) internal pure returns (bool, uint128) { uint128 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint128 a, uint128 b) internal pure returns (bool, uint128) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint128 a, uint128 b) internal pure returns (bool, uint128) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint128 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint128 a, uint128 b) internal pure returns (bool, uint128) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint128 a, uint128 b) internal pure returns (bool, uint128) { if (b == 0) return (false, 0); return (true, a % b); } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint128 a, uint128 b) internal pure returns (uint128) { uint128 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint128 a, uint128 b) internal pure returns (uint128) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint128 a, uint128 b) internal pure returns (uint128) { if (a == 0) return 0; uint128 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint128 a, uint128 b) internal pure returns (uint128) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint128 a, uint128 b) internal pure returns (uint128) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint128 a, uint128 b, string memory errorMessage) internal pure returns (uint128) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint128 a, uint128 b, string memory errorMessage) internal pure returns (uint128) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint128 a, uint128 b, string memory errorMessage) internal pure returns (uint128) { require(b > 0, errorMessage); return a % b; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @author Publius * @title LibSafeMath32 is a uint32 variation of Open Zeppelin's Safe Math library. **/ library LibSafeMath32 { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint32 a, uint32 b) internal pure returns (bool, uint32) { uint32 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint32 a, uint32 b) internal pure returns (bool, uint32) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint32 a, uint32 b) internal pure returns (bool, uint32) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint32 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint32 a, uint32 b) internal pure returns (bool, uint32) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint32 a, uint32 b) internal pure returns (bool, uint32) { if (b == 0) return (false, 0); return (true, a % b); } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint32 a, uint32 b) internal pure returns (uint32) { uint32 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint32 a, uint32 b) internal pure returns (uint32) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint32 a, uint32 b) internal pure returns (uint32) { if (a == 0) return 0; uint32 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint32 a, uint32 b) internal pure returns (uint32) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint32 a, uint32 b) internal pure returns (uint32) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint32 a, uint32 b, string memory errorMessage) internal pure returns (uint32) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint32 a, uint32 b, string memory errorMessage) internal pure returns (uint32) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint32 a, uint32 b, string memory errorMessage) internal pure returns (uint32) { require(b > 0, errorMessage); return a % b; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @title SignedSafeMath * @dev Signed math operations with safety checks that revert on error. */ library LibSafeMathSigned128 { int128 constant private _INT128_MIN = -2**127; /** * @dev Returns the multiplication of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(int128 a, int128 b) internal pure returns (int128) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } require(!(a == -1 && b == _INT128_MIN), "SignedSafeMath: multiplication overflow"); int128 c = a * b; require(c / a == b, "SignedSafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two signed integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(int128 a, int128 b) internal pure returns (int128) { require(b != 0, "SignedSafeMath: division by zero"); require(!(b == -1 && a == _INT128_MIN), "SignedSafeMath: division overflow"); int128 c = a / b; return c; } /** * @dev Returns the subtraction of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(int128 a, int128 b) internal pure returns (int128) { int128 c = a - b; require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow"); return c; } /** * @dev Returns the addition of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(int128 a, int128 b) internal pure returns (int128) { int128 c = a + b; require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow"); return c; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @title SignedSafeMath * @dev Signed math operations with safety checks that revert on error. */ library LibSafeMathSigned96 { int96 constant private _INT96_MIN = -2**95; /** * @dev Returns the multiplication of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(int96 a, int96 b) internal pure returns (int96) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } require(!(a == -1 && b == _INT96_MIN), "SignedSafeMath: multiplication overflow"); int96 c = a * b; require(c / a == b, "SignedSafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two signed integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(int96 a, int96 b) internal pure returns (int96) { require(b != 0, "SignedSafeMath: division by zero"); require(!(b == -1 && a == _INT96_MIN), "SignedSafeMath: division overflow"); int96 c = a / b; return c; } /** * @dev Returns the subtraction of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(int96 a, int96 b) internal pure returns (int96) { int96 c = a - b; require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow"); return c; } /** * @dev Returns the addition of two signed integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(int96 a, int96 b) internal pure returns (int96) { int96 c = a + b; require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow"); return c; } }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {IBean} from "../interfaces/IBean.sol"; import {AppStorage, LibAppStorage} from "./LibAppStorage.sol"; import {C} from "../C.sol"; import {LibWell} from "./Well/LibWell.sol"; import {Call, IWell} from "contracts/interfaces/basin/IWell.sol"; import {IWellFunction} from "contracts/interfaces/basin/IWellFunction.sol"; import {LibLockedUnderlying} from "./LibLockedUnderlying.sol"; /** * @title LibUnripe * @author Publius * @notice Library for handling functionality related to Unripe Tokens and their Ripe Tokens. */ library LibUnripe { using SafeMath for uint256; event ChangeUnderlying(address indexed token, int256 underlying); event SwitchUnderlyingToken(address indexed token, address indexed underlyingToken); uint256 constant DECIMALS = 1e6; /** * @notice Returns the percentage that Unripe Beans have been recapitalized. */ function percentBeansRecapped() internal view returns (uint256 percent) { AppStorage storage s = LibAppStorage.diamondStorage(); return s.u[C.UNRIPE_BEAN].balanceOfUnderlying.mul(DECIMALS).div(C.unripeBean().totalSupply()); } /** * @notice Returns the percentage that Unripe LP have been recapitalized. */ function percentLPRecapped() internal view returns (uint256 percent) { AppStorage storage s = LibAppStorage.diamondStorage(); return C.unripeLPPerDollar().mul(s.recapitalized).div(C.unripeLP().totalSupply()); } /** * @notice Increments the underlying balance of an Unripe Token. * @param token The address of the unripe token. * @param amount The amount of the of the unripe token to be added to the storage reserves */ function incrementUnderlying(address token, uint256 amount) internal { AppStorage storage s = LibAppStorage.diamondStorage(); s.u[token].balanceOfUnderlying = s.u[token].balanceOfUnderlying.add(amount); emit ChangeUnderlying(token, int256(amount)); } /** * @notice Decrements the underlying balance of an Unripe Token. * @param token The address of the Unripe Token. * @param amount The amount of the of the Unripe Token to be removed from storage reserves */ function decrementUnderlying(address token, uint256 amount) internal { AppStorage storage s = LibAppStorage.diamondStorage(); s.u[token].balanceOfUnderlying = s.u[token].balanceOfUnderlying.sub(amount); emit ChangeUnderlying(token, -int256(amount)); } /** * @notice Calculates the amount of Ripe Tokens that underly a given amount of Unripe Tokens. * @param unripeToken The address of the Unripe Token * @param unripe The amount of Unripe Tokens. * @return underlying The amount of Ripe Tokens that underly the Unripe Tokens. */ function unripeToUnderlying( address unripeToken, uint256 unripe, uint256 supply ) internal view returns (uint256 underlying) { AppStorage storage s = LibAppStorage.diamondStorage(); underlying = s.u[unripeToken].balanceOfUnderlying.mul(unripe).div(supply); } /** * @notice Calculates the amount of Unripe Tokens that are underlaid by a given amount of Ripe Tokens. * @param unripeToken The address of the Unripe Tokens. * @param underlying The amount of Ripe Tokens. * @return unripe The amount of the of the Unripe Tokens that are underlaid by the Ripe Tokens. */ function underlyingToUnripe( address unripeToken, uint256 underlying ) internal view returns (uint256 unripe) { AppStorage storage s = LibAppStorage.diamondStorage(); unripe = IBean(unripeToken).totalSupply().mul(underlying).div( s.u[unripeToken].balanceOfUnderlying ); } /** * @notice Adds Ripe Tokens to an Unripe Token. Also, increments the recapitalized * amount proportionally if the Unripe Token is Unripe LP. * @param token The address of the Unripe Token to add Ripe Tokens to. * @param underlying The amount of the of the underlying token to be taken as input. */ function addUnderlying(address token, uint256 underlying) internal { AppStorage storage s = LibAppStorage.diamondStorage(); if (token == C.UNRIPE_LP) { uint256 recapped = underlying.mul(s.recapitalized).div( s.u[C.UNRIPE_LP].balanceOfUnderlying ); s.recapitalized = s.recapitalized.add(recapped); } incrementUnderlying(token, underlying); } /** * @notice Removes Ripe Tokens from an Unripe Token. Also, decrements the recapitalized * amount proportionally if the Unripe Token is Unripe LP. * @param token The address of the unripe token to be removed. * @param underlying The amount of the of the underlying token to be removed. */ function removeUnderlying(address token, uint256 underlying) internal { AppStorage storage s = LibAppStorage.diamondStorage(); if (token == C.UNRIPE_LP) { uint256 recapped = underlying.mul(s.recapitalized).div( s.u[C.UNRIPE_LP].balanceOfUnderlying ); s.recapitalized = s.recapitalized.sub(recapped); } decrementUnderlying(token, underlying); } /** * @dev Switches the underlying token of an unripe token. * Should only be called if `s.u[unripeToken].balanceOfUnderlying == 0`. */ function switchUnderlyingToken(address unripeToken, address newUnderlyingToken) internal { AppStorage storage s = LibAppStorage.diamondStorage(); s.u[unripeToken].underlyingToken = newUnderlyingToken; emit SwitchUnderlyingToken(unripeToken, newUnderlyingToken); } function _getPenalizedUnderlying( address unripeToken, uint256 amount, uint256 supply ) internal view returns (uint256 redeem) { require(isUnripe(unripeToken), "not vesting"); uint256 sharesBeingRedeemed = getRecapPaidPercentAmount(amount); redeem = _getUnderlying(unripeToken, sharesBeingRedeemed, supply); } /** * @notice Calculates the the amount of Ripe Tokens that would be paid out if * all Unripe Tokens were Chopped at the current Chop Rate. */ function _getTotalPenalizedUnderlying( address unripeToken ) internal view returns (uint256 redeem) { require(isUnripe(unripeToken), "not vesting"); uint256 supply = IERC20(unripeToken).totalSupply(); redeem = _getUnderlying(unripeToken, getRecapPaidPercentAmount(supply), supply); } /** * @notice Returns the amount of beans that are locked in the unripe token. * @dev Locked beans are the beans that are forfeited if the unripe token is chopped. * @param reserves the reserves of the LP that underly the unripe token. * @dev reserves are used as a parameter for gas effiency purposes (see LibEvaluate.calcLPToSupplyRatio}. */ function getLockedBeans( uint256[] memory reserves ) internal view returns (uint256 lockedAmount) { lockedAmount = LibLockedUnderlying .getLockedUnderlying(C.UNRIPE_BEAN, getRecapPaidPercentAmount(1e6)) .add(getLockedBeansFromLP(reserves)); } /** * @notice Returns the amount of beans that are locked in the unripeLP token. * @param reserves the reserves of the LP that underly the unripe token. */ function getLockedBeansFromLP( uint256[] memory reserves ) internal view returns (uint256 lockedBeanAmount) { AppStorage storage s = LibAppStorage.diamondStorage(); // if reserves return 0, then skip calculations. if (reserves[0] == 0) return 0; uint256 lockedLpAmount = LibLockedUnderlying.getLockedUnderlying( C.UNRIPE_LP, getRecapPaidPercentAmount(1e6) ); address underlying = s.u[C.UNRIPE_LP].underlyingToken; uint256 beanIndex = LibWell.getBeanIndexFromWell(underlying); // lpTokenSupply is calculated rather than calling totalSupply(), // because the Well's lpTokenSupply is not MEV resistant. Call memory wellFunction = IWell(underlying).wellFunction(); uint lpTokenSupply = IWellFunction(wellFunction.target).calcLpTokenSupply( reserves, wellFunction.data ); lockedBeanAmount = lockedLpAmount.mul(reserves[beanIndex]).div(lpTokenSupply); } /** * @notice Calculates the penalized amount based the amount of Sprouts that are Rinsable * or Rinsed (Fertilized). * @param amount The amount of the Unripe Tokens. * @return penalizedAmount The penalized amount of the Ripe Tokens received from Chopping. */ function getRecapPaidPercentAmount( uint256 amount ) internal view returns (uint256 penalizedAmount) { AppStorage storage s = LibAppStorage.diamondStorage(); return s.fertilizedIndex.mul(amount).div(s.unfertilizedIndex); } /** * @notice Returns true if the token is unripe. */ function isUnripe(address unripeToken) internal view returns (bool unripe) { AppStorage storage s = LibAppStorage.diamondStorage(); unripe = s.u[unripeToken].underlyingToken != address(0); } /** * @notice Returns the underlying token amount of the unripe token. */ function _getUnderlying( address unripeToken, uint256 amount, uint256 supply ) internal view returns (uint256 redeem) { AppStorage storage s = LibAppStorage.diamondStorage(); redeem = s.u[unripeToken].balanceOfUnderlying.mul(amount).div(supply); } }
/** * SPDX-License-Identifier: MIT **/ pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {C} from "contracts/C.sol"; /** * @title Minting Library * @notice Contains Helper Fucntions for Minting related functionality. **/ library LibMinting { using SafeMath for uint256; uint256 private constant MAX_DELTA_B_DENOMINATOR = 100; function checkForMaxDeltaB(int256 deltaB) internal view returns (int256) { int256 maxDeltaB = int256(C.bean().totalSupply().div(MAX_DELTA_B_DENOMINATOR)); if (deltaB < 0) return deltaB > -maxDeltaB ? deltaB : -maxDeltaB; return deltaB < maxDeltaB ? deltaB : maxDeltaB; } }
/** * SPDX-License-Identifier: MIT **/ pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {LibAppStorage, AppStorage} from "../LibAppStorage.sol"; import {SafeMath, C, LibMinting} from "./LibMinting.sol"; import {ICumulativePump} from "contracts/interfaces/basin/pumps/ICumulativePump.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Call, IWell} from "contracts/interfaces/basin/IWell.sol"; import {LibWell} from "contracts/libraries/Well/LibWell.sol"; import {IBeanstalkWellFunction} from "contracts/interfaces/basin/IBeanstalkWellFunction.sol"; import {SignedSafeMath} from "@openzeppelin/contracts/math/SignedSafeMath.sol"; import {LibEthUsdOracle} from "contracts/libraries/Oracle/LibEthUsdOracle.sol"; /** * @title Well Minting Oracle Library * @notice Well Minting Oracle can be Checked or Captured to compute * the time weighted average Delta B since the last time the Oracle was Captured * for a given Well. * * @dev * The Oracle uses the Season timestamp stored in `s.season.timestamp` to determine how many seconds * it has been since the last Season instead of storing its own for efficiency purposes. * Each Capture stores the encoded cumulative reserves returned by the Pump in `s.wellOracleSnapshots[well]`. **/ library LibWellMinting { using SignedSafeMath for int256; /** * @notice Emitted when a Well Minting Oracle is captured. * @param season The season that the Well was captured. * @param well The Well that was captured. * @param deltaB The time weighted average delta B computed during the Oracle capture. * @param cumulativeReserves The encoded cumulative reserves that were snapshotted most by the Oracle capture. */ event WellOracle( uint32 indexed season, address well, int256 deltaB, bytes cumulativeReserves ); using SafeMath for uint256; //////////////////// CHECK //////////////////// /** * @dev Returns the time weighted average delta B in a given Well * since the last Sunrise. * @return deltaB The time weighted average delta B balance since the last `capture` call. */ function check( address well ) external view returns (int256 deltaB) { bytes memory lastSnapshot = LibAppStorage .diamondStorage() .wellOracleSnapshots[well]; // If the length of the stored Snapshot for a given Well is 0, // then the Oracle is not initialized. if (lastSnapshot.length > 0) { (deltaB, , , ) = twaDeltaB(well, lastSnapshot); } deltaB = LibMinting.checkForMaxDeltaB(deltaB); } //////////////////// CHECK //////////////////// /** * @dev Returns the time weighted average delta B in a given Well * since the last Sunrise and snapshots the current cumulative reserves. * @return deltaB The time weighted average delta B balance since the last `capture` call. */ function capture( address well ) external returns (int256 deltaB) { bytes memory lastSnapshot = LibAppStorage .diamondStorage() .wellOracleSnapshots[well]; // If the length of the stored Snapshot for a given Well is 0, // then the Oracle is not initialized. if (lastSnapshot.length > 0) { deltaB = updateOracle(well, lastSnapshot); } else { initializeOracle(well); } deltaB = LibMinting.checkForMaxDeltaB(deltaB); } //////////////////// Oracle //////////////////// /** * Initializes the Well Minting Oracle for a given Well by snapshotting the current * encoded cumulative reserves from a Beanstalk supported pump. */ function initializeOracle(address well) internal { AppStorage storage s = LibAppStorage.diamondStorage(); // Given Multi Flow Pump V 1.0 isn't resistant to large changes in balance, // minting in the Bean:Eth Well needs to be turned off upon migration. if (!checkShouldTurnOnMinting(well)) { return; } // If pump has not been initialized for `well`, `readCumulativeReserves` will revert. // Need to handle failure gracefully, so Sunrise does not revert. Call[] memory pumps = IWell(well).pumps(); try ICumulativePump(pumps[0].target).readCumulativeReserves( well, pumps[0].data ) returns (bytes memory lastSnapshot) { s.wellOracleSnapshots[well] = lastSnapshot; emit WellOracle(s.season.current, well, 0, lastSnapshot); } catch { emit WellOracle(s.season.current, well, 0, new bytes(0)); } } /** * @dev Updates the Oracle snapshot for a given Well and returns the deltaB * given the previous snapshot in the Well */ function updateOracle( address well, bytes memory lastSnapshot ) internal returns (int256 deltaB) { AppStorage storage s = LibAppStorage.diamondStorage(); uint256[] memory twaReserves; uint256[] memory ratios; (deltaB, s.wellOracleSnapshots[well], twaReserves, ratios) = twaDeltaB( well, lastSnapshot ); // Set the Well reserves in storage, so that it can be read when // 1) set the USD price of the non bean token so that it can be read when // calculating the price of Bean. See {LibEvaluate.evalPrice}. // 2) When calculating the Bean reward for calling the Season (Bean:Eth Only). // See {LibIncentive.determineReward}. LibWell.setTwaReservesForWell(well, twaReserves); LibWell.setUsdTokenPriceForWell(well, ratios); emit WellOracle( s.season.current, well, deltaB, s.wellOracleSnapshots[well] ); } /** * @dev Calculates the time weighted average delta B since the input snapshot for * a given Well address. */ function twaDeltaB( address well, bytes memory lastSnapshot ) internal view returns (int256, bytes memory, uint256[] memory, uint256[] memory) { AppStorage storage s = LibAppStorage.diamondStorage(); // Try to call `readTwaReserves` and handle failure gracefully, so Sunrise does not revert. // On failure, reset the Oracle by returning an empty snapshot and a delta B of 0. Call[] memory pumps = IWell(well).pumps(); try ICumulativePump(pumps[0].target).readTwaReserves( well, lastSnapshot, uint40(s.season.timestamp), pumps[0].data ) returns (uint[] memory twaReserves, bytes memory snapshot) { IERC20[] memory tokens = IWell(well).tokens(); ( uint256[] memory ratios, uint256 beanIndex, bool success ) = LibWell.getRatiosAndBeanIndex(tokens, block.timestamp.sub(s.season.timestamp)); // If the Bean reserve is less than the minimum, the minting oracle should be considered off. if (twaReserves[beanIndex] < C.WELL_MINIMUM_BEAN_BALANCE) { return (0, snapshot, new uint256[](0), new uint256[](0)); } // If the USD Oracle oracle call fails, the minting oracle should be considered off. if (!success) { return (0, snapshot, twaReserves, new uint256[](0)); } Call memory wellFunction = IWell(well).wellFunction(); // Delta B is the difference between the target Bean reserve at the peg price // and the time weighted average Bean balance in the Well. int256 deltaB = int256(IBeanstalkWellFunction(wellFunction.target).calcReserveAtRatioSwap( twaReserves, beanIndex, ratios, wellFunction.data )).sub(int256(twaReserves[beanIndex])); return (deltaB, snapshot, twaReserves, ratios); } catch { // if the pump fails, return all 0s to avoid the sunrise reverting. return (0, new bytes(0), new uint256[](0), new uint256[](0)); } } // Remove in next BIP. function checkShouldTurnOnMinting(address well) internal view returns (bool) { AppStorage storage s = LibAppStorage.diamondStorage(); if (well == C.BEAN_ETH_WELL) { if (s.season.current < s.season.beanEthStartMintingSeason) { return false; } } return true; } }
/** * SPDX-License-Identifier: MIT **/ pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {C} from "contracts/C.sol"; import {IChainlinkAggregator} from "contracts/interfaces/chainlink/IChainlinkAggregator.sol"; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; /** * @title Chainlink Oracle Library * @notice Contains functionalty to fetch prices from Chainlink price feeds. * @dev currently supports: * - ETH/USD price feed **/ library LibChainlinkOracle { using SafeMath for uint256; uint256 constant PRECISION = 1e6; // use 6 decimal precision. // timeout for Oracles with a 1 hour heartbeat. uint256 constant FOUR_HOUR_TIMEOUT = 14400; // timeout for Oracles with a 1 day heartbeat. uint256 constant FOUR_DAY_TIMEOUT = 345600; struct TwapVariables { uint256 cumulativePrice; uint256 endTimestamp; uint256 lastTimestamp; } /** * @dev Returns the price of a given `priceAggregator` * Return value has 6 decimal precision. * Returns 0 if Chainlink's price feed is broken or frozen. **/ function getPrice( address priceAggregatorAddress, uint256 maxTimeout ) internal view returns (uint256 price) { IChainlinkAggregator priceAggregator = IChainlinkAggregator(priceAggregatorAddress); // First, try to get current decimal precision: uint8 decimals; try priceAggregator.decimals() returns (uint8 _decimals) { // If call to Chainlink succeeds, record the current decimal precision decimals = _decimals; } catch { // If call to Chainlink aggregator reverts, return a price of 0 indicating failure return 0; } // Secondly, try to get latest price data: try priceAggregator.latestRoundData() returns ( uint80 roundId, int256 answer, uint256 /* startedAt */, uint256 timestamp, uint80 /* answeredInRound */ ) { // Check for an invalid roundId that is 0 if (roundId == 0) return 0; if (checkForInvalidTimestampOrAnswer(timestamp, answer, block.timestamp, maxTimeout)) { return 0; } // Adjust to 6 decimal precision. return uint256(answer).mul(PRECISION).div(10 ** decimals); } catch { // If call to Chainlink aggregator reverts, return a price of 0 indicating failure return 0; } } /** * @dev Returns the TWAP price from the Chainlink Oracle over the past `lookback` seconds. * Return value has 6 decimal precision. * Returns 0 if Chainlink's price feed is broken or frozen. **/ function getTwap( address priceAggregatorAddress, uint256 maxTimeout, uint256 lookback ) internal view returns (uint256 price) { IChainlinkAggregator priceAggregator = IChainlinkAggregator(priceAggregatorAddress); // First, try to get current decimal precision: uint8 decimals; try priceAggregator.decimals() returns (uint8 _decimals) { // If call to Chainlink succeeds, record the current decimal precision decimals = _decimals; } catch { // If call to Chainlink aggregator reverts, return a price of 0 indicating failure return 0; } // Secondly, try to get latest price data: try priceAggregator.latestRoundData() returns ( uint80 roundId, int256 answer, uint256 /* startedAt */, uint256 timestamp, uint80 /* answeredInRound */ ) { // Check for an invalid roundId that is 0 if (roundId == 0) return 0; if (checkForInvalidTimestampOrAnswer(timestamp, answer, block.timestamp, maxTimeout)) { return 0; } TwapVariables memory t; t.endTimestamp = block.timestamp.sub(lookback); // Check if last round was more than `lookback` ago. if (timestamp <= t.endTimestamp) { return uint256(answer).mul(PRECISION).div(10 ** decimals); } else { t.lastTimestamp = block.timestamp; // Loop through previous rounds and compute cumulative sum until // a round at least `lookback` seconds ago is reached. while (timestamp > t.endTimestamp) { t.cumulativePrice = t.cumulativePrice.add( uint256(answer).mul(t.lastTimestamp.sub(timestamp)) ); roundId -= 1; t.lastTimestamp = timestamp; (answer, timestamp) = getRoundData(priceAggregator, roundId); if (checkForInvalidTimestampOrAnswer( timestamp, answer, t.lastTimestamp, maxTimeout )) { return 0; } } t.cumulativePrice = t.cumulativePrice.add( uint256(answer).mul(t.lastTimestamp.sub(t.endTimestamp)) ); return t.cumulativePrice.mul(PRECISION).div(10 ** decimals).div(lookback); } } catch { // If call to Chainlink aggregator reverts, return a price of 0 indicating failure return 0; } } function getRoundData( IChainlinkAggregator priceAggregator, uint80 roundId ) private view returns (int256, uint256) { try priceAggregator.getRoundData(roundId) returns ( uint80 /* roundId */, int256 _answer, uint256 /* startedAt */, uint256 _timestamp, uint80 /* answeredInRound */ ) { return (_answer, _timestamp); } catch { return (-1, 0); } } function checkForInvalidTimestampOrAnswer( uint256 timestamp, int256 answer, uint256 currentTimestamp, uint256 maxTimeout ) private pure returns (bool) { // Check for an invalid timeStamp that is 0, or in the future if (timestamp == 0 || timestamp > currentTimestamp) return true; // Check if Chainlink's price feed has timed out if (currentTimestamp.sub(timestamp) > maxTimeout) return true; // Check for non-positive price if (answer <= 0) return true; } }
/** * SPDX-License-Identifier: MIT **/ pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {LibChainlinkOracle} from "./LibChainlinkOracle.sol"; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {LibAppStorage, AppStorage} from "contracts/libraries/LibAppStorage.sol"; import {C} from "contracts/C.sol"; import {LibOracleHelpers} from "contracts/libraries/Oracle/LibOracleHelpers.sol"; /** * @title Eth Usd Oracle Library * @notice Contains functionalty to fetch a manipulation resistant ETH/USD price. * @dev * The Oracle uses the ETH/USD Chainlink Oracle to fetch the price. * The oracle will fail (return 0) if the Chainlink Oracle is broken or frozen (See: {LibChainlinkOracle}). **/ library LibEthUsdOracle { using SafeMath for uint256; /////////////////// ORACLES /////////////////// address constant ETH_USD_CHAINLINK_PRICE_AGGREGATOR = 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419; /////////////////////////////////////////////// function getEthUsdPriceFromStorageIfSaved() internal view returns (uint256) { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 priceInStorage = s.usdTokenPrice[C.BEAN_ETH_WELL]; if (priceInStorage == 1) { return getEthUsdPrice(); } return priceInStorage; } /** * @dev Returns the instantaneous ETH/USD price * Return value has 6 decimal precision. * Returns 0 if the ETH/USD Chainlink Oracle is broken or frozen. **/ function getEthUsdPrice() internal view returns (uint256) { return LibChainlinkOracle.getPrice(ETH_USD_CHAINLINK_PRICE_AGGREGATOR, LibChainlinkOracle.FOUR_HOUR_TIMEOUT); } /** * @dev Returns the ETH/USD price with the option of using a TWA lookback. * Use `lookback = 0` for the instantaneous price. `lookback > 0` for a TWAP. * Return value has 6 decimal precision. * Returns 0 if the ETH/USD Chainlink Oracle is broken or frozen. **/ function getEthUsdPrice(uint256 lookback) internal view returns (uint256) { return lookback > 0 ? LibChainlinkOracle.getTwap( ETH_USD_CHAINLINK_PRICE_AGGREGATOR, LibChainlinkOracle.FOUR_HOUR_TIMEOUT, lookback ) : LibChainlinkOracle.getPrice( ETH_USD_CHAINLINK_PRICE_AGGREGATOR, LibChainlinkOracle.FOUR_HOUR_TIMEOUT ); } }
/** * SPDX-License-Identifier: MIT **/ pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; /** * @title Oracle Helpers Library * @author brendan * @notice Contains functionalty common to multiple Oracle libraries. **/ library LibOracleHelpers { using SafeMath for uint256; uint256 constant ONE = 1e18; /** * Gets the percent difference between two values with 18 decimal precision. * @dev If x == 0 (Such as in the case of Uniswap Oracle failure), then the percent difference is calculated as 100%. * Always use the bigger price as the denominator, thereby making sure that in whichever of the two cases explained in audit report (M-03), * i.e if x > y or not a fixed percentDifference is provided and this can then be accurately checked against protocol's set MAX_DIFFERENCE value. */ function getPercentDifference( uint x, uint y ) internal pure returns (uint256 percentDifference) { if (x == y) { percentDifference = 0; } else if (x < y) { percentDifference = x.mul(ONE).div(y); percentDifference = ONE - percentDifference; } else { percentDifference = y.mul(ONE).div(x); percentDifference = ONE - percentDifference; } return percentDifference; } }
/** * SPDX-License-Identifier: MIT **/ pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {C} from "contracts/C.sol"; import {OracleLibrary} from "@uniswap/v3-periphery/contracts/libraries/OracleLibrary.sol"; import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol'; /** * @title Uniswap Oracle Library * @notice Contains functionalty to read prices from Uniswap V3 pools. * @dev currently supports: * - ETH:USDC price from the ETH:USDC 0.05% pool * - ETH:USDT price from the ETH:USDT 0.05% pool **/ library LibUniswapOracle { // All instantaneous queries of Uniswap Oracles should use a 15 minute lookback. uint32 constant internal FIFTEEN_MINUTES = 900; /** * @dev Uses `pool`'s Uniswap V3 Oracle to get the TWAP price of `token1` in `token2` over the * last `lookback` seconds. * Return value has 6 decimal precision. * Returns 0 if {IUniswapV3Pool.observe} reverts. */ function getTwap(uint32 lookback, address pool, address token1, address token2, uint128 oneToken) internal view returns (uint256 price) { (bool success, int24 tick) = consult(pool, lookback); if (!success) return 0; price = OracleLibrary.getQuoteAtTick(tick, oneToken, token1, token2); } /** * @dev A variation of {OracleLibrary.consult} that returns just the arithmetic mean tick and returns 0 on failure * instead of reverting if {IUniswapV3Pool.observe} reverts. * https://github.com/Uniswap/v3-periphery/blob/51f8871aaef2263c8e8bbf4f3410880b6162cdea/contracts/libraries/OracleLibrary.sol */ function consult(address pool, uint32 secondsAgo) internal view returns (bool success, int24 arithmeticMeanTick) { require(secondsAgo != 0, 'BP'); uint32[] memory secondsAgos = new uint32[](2); secondsAgos[0] = secondsAgo; secondsAgos[1] = 0; try IUniswapV3Pool(pool).observe(secondsAgos) returns ( int56[] memory tickCumulatives, uint160[] memory ) { int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0]; arithmeticMeanTick = int24(tickCumulativesDelta / secondsAgo); // Always round to negative infinity if (tickCumulativesDelta < 0 && (tickCumulativesDelta % secondsAgo != 0)) arithmeticMeanTick--; success = true; } catch {} } }
/** * SPDX-License-Identifier: MIT **/ pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {LibEthUsdOracle} from "./LibEthUsdOracle.sol"; import {LibWstethUsdOracle} from "./LibWstethUsdOracle.sol"; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {C} from "contracts/C.sol"; /** * @title Eth Usd Oracle Library * @notice Contains functionalty to fetch the manipulation resistant USD price of different tokens. * @dev currently supports: * - ETH/USD price **/ library LibUsdOracle { using SafeMath for uint256; function getUsdPrice(address token) internal view returns (uint256) { return getUsdPrice(token, 0); } /** * @dev Returns the price of a given token in in USD with the option of using a lookback. (Usd:token Price) * `lookback` should be 0 if the instantaneous price is desired. Otherwise, it should be the * TWAP lookback in seconds. * If using a non-zero lookback, it is recommended to use a substantially large `lookback` * (> 900 seconds) to protect against manipulation. */ function getUsdPrice(address token, uint256 lookback) internal view returns (uint256) { if (token == C.WETH) { uint256 ethUsdPrice = LibEthUsdOracle.getEthUsdPrice(lookback); if (ethUsdPrice == 0) return 0; return uint256(1e24).div(ethUsdPrice); } if (token == C.WSTETH) { uint256 wstethUsdPrice = LibWstethUsdOracle.getWstethUsdPrice(lookback); if (wstethUsdPrice == 0) return 0; return uint256(1e24).div(wstethUsdPrice); } revert("Oracle: Token not supported."); } function getTokenPrice(address token) internal view returns (uint256) { return getTokenPrice(token, 0); } /** * @notice returns the price of a given token in USD (token:Usd Price) * @dev if ETH returns 1000 USD, this function returns 1000 * (ignoring decimal precision) */ function getTokenPrice(address token, uint256 lookback) internal view returns (uint256) { if (token == C.WETH) { uint256 ethUsdPrice = LibEthUsdOracle.getEthUsdPrice(lookback); if (ethUsdPrice == 0) return 0; return ethUsdPrice; } if (token == C.WSTETH) { uint256 wstethUsdPrice = LibWstethUsdOracle.getWstethUsdPrice(lookback); if (wstethUsdPrice == 0) return 0; return wstethUsdPrice; } revert("Oracle: Token not supported."); } }
/** * SPDX-License-Identifier: MIT **/ pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {LibChainlinkOracle} from "./LibChainlinkOracle.sol"; import {LibUniswapOracle} from "./LibUniswapOracle.sol"; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {LibAppStorage, AppStorage} from "contracts/libraries/LibAppStorage.sol"; import {C} from "contracts/C.sol"; import {LibOracleHelpers} from "contracts/libraries/Oracle/LibOracleHelpers.sol"; interface IWsteth { function stEthPerToken() external view returns (uint256); } /** * @title Wsteth Eth Oracle Library * @author brendan * @notice Computes the wstETH:ETH price. * @dev * The oracle reads from 4 data sources: * a. wstETH:stETH Redemption Rate: (0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0) * b. stETH:ETH Chainlink Oracle: (0x86392dC19c0b719886221c78AB11eb8Cf5c52812) * c. wstETH:ETH Uniswap Pool: (0x109830a1AAaD605BbF02a9dFA7B0B92EC2FB7dAa) * d. stETH:ETH Redemption: (1:1) * * It then computes the wstETH:ETH price in 3 ways: * 1. wstETH -> ETH via Chainlink: a * b * 2. wstETH -> ETH via wstETH:ETH Uniswap Pool: c * 1 * 3. wstETH -> ETH via stETH redemption: a * d * * It then computes a wstETH:ETH price by taking the minimum of (3) and either the average of (1) and (2) * if (1) and (2) are within `MAX_DIFFERENCE` from each other or (1). **/ library LibWstethEthOracle { using SafeMath for uint256; // The maximum percent difference such that the oracle assumes no manipulation is occuring. uint256 constant MAX_DIFFERENCE = 0.01e18; // 1% uint256 constant CHAINLINK_DENOMINATOR = 1e6; uint128 constant ONE = 1e18; uint128 constant AVERAGE_DENOMINATOR = 2; uint128 constant PRECISION_DENOMINATOR = 1e12; /////////////////// ORACLES /////////////////// address constant WSTETH_ETH_CHAINLINK_PRICE_AGGREGATOR = 0x86392dC19c0b719886221c78AB11eb8Cf5c52812; address internal constant WSTETH_ETH_UNIV3_01_POOL = 0x109830a1AAaD605BbF02a9dFA7B0B92EC2FB7dAa; // 0.01% pool /////////////////////////////////////////////// /** * @dev Returns the instantaneous wstETH/ETH price * Return value has 6 decimal precision. * Returns 0 if the either the Chainlink Oracle or Uniswap Oracle cannot fetch a valid price. **/ function getWstethEthPrice() internal view returns (uint256) { return getWstethEthPrice(0); } /** * @dev Returns the wstETH/ETH price with the option of using a TWA lookback. * Return value has 6 decimal precision. * Returns 0 if the either the Chainlink Oracle or Uniswap Oracle cannot fetch a valid price. **/ function getWstethEthPrice(uint256 lookback) internal view returns (uint256 wstethEthPrice) { uint256 chainlinkPrice = lookback == 0 ? LibChainlinkOracle.getPrice(WSTETH_ETH_CHAINLINK_PRICE_AGGREGATOR, LibChainlinkOracle.FOUR_DAY_TIMEOUT) : LibChainlinkOracle.getTwap(WSTETH_ETH_CHAINLINK_PRICE_AGGREGATOR, LibChainlinkOracle.FOUR_DAY_TIMEOUT, lookback); // Check if the chainlink price is broken or frozen. if (chainlinkPrice == 0) return 0; uint256 stethPerWsteth = IWsteth(C.WSTETH).stEthPerToken(); chainlinkPrice = chainlinkPrice.mul(stethPerWsteth).div(CHAINLINK_DENOMINATOR); // Uniswap V3 only supports a uint32 lookback. if (lookback > type(uint32).max) return 0; uint256 uniswapPrice = LibUniswapOracle.getTwap( lookback == 0 ? LibUniswapOracle.FIFTEEN_MINUTES : uint32(lookback), WSTETH_ETH_UNIV3_01_POOL, C.WSTETH, C.WETH, ONE ); // Check if the uniswapPrice oracle fails. if (uniswapPrice == 0) return 0; if (LibOracleHelpers.getPercentDifference(chainlinkPrice, uniswapPrice) < MAX_DIFFERENCE) { wstethEthPrice = chainlinkPrice.add(uniswapPrice).div(AVERAGE_DENOMINATOR); if (wstethEthPrice > stethPerWsteth) wstethEthPrice = stethPerWsteth; wstethEthPrice = wstethEthPrice.div(PRECISION_DENOMINATOR); } } }
/** * SPDX-License-Identifier: MIT **/ pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {IWsteth, LibWstethEthOracle, SafeMath} from "contracts/libraries/Oracle/LibWstethEthOracle.sol"; import {LibEthUsdOracle} from "contracts/libraries/Oracle/LibEthUsdOracle.sol"; /** * @title Wsteth USD Oracle Library * @author brendan * @notice Computes the wStETH:USD price. * @dev * The oracle reads from 2 data sources: * a. LibWstethEthOracle * b. LibEthUsdOracle * * The wStEth:USD price is computed as: a * b **/ library LibWstethUsdOracle { using SafeMath for uint256; uint256 constant ORACLE_PRECISION = 1e6; /** * @dev Returns the instantaneous wstETH/USD price * Return value has 6 decimal precision. * Returns 0 if the either LibWstethEthOracle or LibEthUsdOracle cannot fetch a valid price. **/ function getWstethUsdPrice() internal view returns (uint256) { return getWstethUsdPrice(0); } /** * @dev Returns the wstETH/USD price with the option of using a TWA lookback. * Return value has 6 decimal precision. * Returns 0 if the either LibWstethEthOracle or LibEthUsdOracle cannot fetch a valid price. **/ function getWstethUsdPrice(uint256 lookback) internal view returns (uint256) { return LibWstethEthOracle.getWstethEthPrice(lookback).mul( LibEthUsdOracle.getEthUsdPrice(lookback) ).div(ORACLE_PRECISION); } }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {LibAppStorage, Storage, AppStorage, Account} from "../LibAppStorage.sol"; import {LibSafeMath128} from "../LibSafeMath128.sol"; import {LibSafeMath32} from "../LibSafeMath32.sol"; import {LibSafeMathSigned96} from "../LibSafeMathSigned96.sol"; import {LibTokenSilo} from "./LibTokenSilo.sol"; import {LibSilo} from "./LibSilo.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/SafeCast.sol"; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {C} from "../../C.sol"; /** * @title LibGerminate * @author Brean * @notice LibGerminate handles logic associated with germination. * @dev "germinating" values are values that are currently inactive. * germinating values stay germinating until the 1 + the remainder of the current season as passed, * in which case they become active. * * The following are germinating: * - newly issued stalk (from new deposits or mowing) * - roots from newly issued stalk * - new bdv introduced in the silo. */ library LibGerminate { using SafeMath for uint256; using SafeCast for uint256; using LibSafeMath32 for uint32; using LibSafeMath128 for uint128; using LibSafeMathSigned96 for int96; //////////////////////// EVENTS //////////////////////// /** * @notice emitted when the farmers germinating stalk changes. */ event FarmerGerminatingStalkBalanceChanged( address indexed account, int256 deltaGerminatingStalk, Germinate germinationState ); /** * @notice emitted when the total germinating amount/bdv changes. * @param germinationSeason the season the germination occured. * Does not always equal the current season. * @param token the token being updated. * @param deltaAmount the change in the total germinating amount. * @param deltaBdv the change in the total germinating bdv. */ event TotalGerminatingBalanceChanged( uint256 germinationSeason, address indexed token, int256 deltaAmount, int256 deltaBdv ); /** * @notice emitted when the total germinating stalk changes. * @param germinationSeason issuance season of germinating stalk * @param deltaGerminatingStalk the change in the total germinating stalk. * @dev the issuance season may differ from the season that this event was emitted in.. */ event TotalGerminatingStalkChanged(uint256 germinationSeason, int256 deltaGerminatingStalk); /** * @notice emitted at the sunrise function when the total stalk and roots are incremented. * @dev currently, stalk and roots can only increase at the end of `endTotalGermination`, * but is casted in the event to allow for future decreases. */ event TotalStalkChangedFromGermination(int256 deltaStalk, int256 deltaRoots); struct GermStem { int96 germinatingStem; int96 stemTip; } /** * @notice Germinate determines what germination struct to use. * @dev "odd" and "even" refers to the value of the season counter. * "Odd" germinations are used when the season is odd, and vice versa. */ enum Germinate { ODD, EVEN, NOT_GERMINATING } /** * @notice Ends the germination process of system-wide values. * @dev we calculate the unclaimed germinating roots of 2 seasons ago * as the roots of the stalk should be calculated based on the total stalk * when germination finishes, rather than when germination starts. */ function endTotalGermination(uint32 season, address[] memory tokens) external { AppStorage storage s = LibAppStorage.diamondStorage(); // germination can only occur after season 3. if (season < 2) return; uint32 germinationSeason = season.sub(2); // base roots are used if there are no roots in the silo. // root calculation is skipped if no deposits have been made // in the season. uint128 finishedGerminatingStalk = s.unclaimedGerminating[germinationSeason].stalk; uint128 rootsFromGerminatingStalk; if (s.s.roots == 0) { rootsFromGerminatingStalk = finishedGerminatingStalk.mul(uint128(C.getRootsBase())); } else if (s.unclaimedGerminating[germinationSeason].stalk > 0) { rootsFromGerminatingStalk = s.s.roots.mul(finishedGerminatingStalk).div(s.s.stalk).toUint128(); } s.unclaimedGerminating[germinationSeason].roots = rootsFromGerminatingStalk; // increment total stalk and roots based on unclaimed values. s.s.stalk = s.s.stalk.add(finishedGerminatingStalk); s.s.roots = s.s.roots.add(rootsFromGerminatingStalk); // increment total deposited and amounts for each token. Storage.TotalGerminating storage totalGerm; if (getSeasonGerminationState() == Germinate.ODD) { totalGerm = s.oddGerminating; } else { totalGerm = s.evenGerminating; } for (uint i; i < tokens.length; ++i) { // if the token has no deposits, skip. if (totalGerm.deposited[tokens[i]].amount == 0) { continue; } LibTokenSilo.incrementTotalDeposited( tokens[i], totalGerm.deposited[tokens[i]].amount, totalGerm.deposited[tokens[i]].bdv ); // emit events. emit TotalGerminatingBalanceChanged( germinationSeason, tokens[i], -int256(totalGerm.deposited[tokens[i]].amount), -int256(totalGerm.deposited[tokens[i]].bdv) ); // clear deposited values. delete totalGerm.deposited[tokens[i]]; } // emit change in total germinating stalk. // safecast not needed as finishedGerminatingStalk is initially a uint128. emit TotalGerminatingStalkChanged(germinationSeason, -int256(finishedGerminatingStalk)); emit TotalStalkChangedFromGermination(int256(finishedGerminatingStalk), int256(rootsFromGerminatingStalk)); } /** * @notice contains logic for ending germination for stalk and roots. * @param account address of the account to end germination for. * @param lastMowedSeason the last season the account mowed. * * @dev `first` refers to the set of germinating stalk * and roots created in the season closest to the current season. * i.e if a user deposited in season 10 and 11, the `first` stalk * would be season 11. * * the germination process: * - increments the assoicated values (bdv, stalk, roots) * - clears the germination struct for the account. * * @return firstGerminatingRoots the roots from the first germinating stalk. * used in {handleRainAndSops} to properly set the rain roots of a user, * if the user had deposited in the season prior to a rain. */ function endAccountGermination( address account, uint32 lastMowedSeason, uint32 currentSeason ) internal returns (uint128 firstGerminatingRoots) { AppStorage storage s = LibAppStorage.diamondStorage(); bool lastUpdateOdd = isSeasonOdd(lastMowedSeason); (uint128 firstStalk, uint128 secondStalk) = getGerminatingStalk(account, lastUpdateOdd); uint128 totalRootsFromGermination; uint128 germinatingStalk; // check to end germination for first stalk. // if last mowed season is greater or equal than (currentSeason - 1), // then the first stalk is still germinating. if (firstStalk > 0 && lastMowedSeason < currentSeason.sub(1)) { (uint128 roots, Germinate germState) = claimGerminatingRoots( account, lastMowedSeason, firstStalk, lastUpdateOdd ); germinatingStalk = firstStalk; totalRootsFromGermination = roots; firstGerminatingRoots = roots; emit FarmerGerminatingStalkBalanceChanged( account, -int256(germinatingStalk), germState ); } // check to end germination for second stalk. if (secondStalk > 0) { (uint128 roots, Germinate germState) = claimGerminatingRoots( account, lastMowedSeason.sub(1), secondStalk, !lastUpdateOdd ); germinatingStalk = germinatingStalk.add(secondStalk); totalRootsFromGermination = totalRootsFromGermination.add(roots); emit FarmerGerminatingStalkBalanceChanged(account, -int256(germinatingStalk), germState); } // increment users stalk and roots. if (germinatingStalk > 0) { s.a[account].s.stalk = s.a[account].s.stalk.add(germinatingStalk); s.a[account].roots = s.a[account].roots.add(totalRootsFromGermination); // emit event. Active stalk is incremented, germinating stalk is decremented. emit LibSilo.StalkBalanceChanged( account, int256(germinatingStalk), int256(totalRootsFromGermination) ); } } /** * @notice Claims the germinating roots of an account, * as well as clears the germinating stalk and roots. * * @param account address of the account to end germination for. * @param season the season to calculate the germinating roots for. * @param stalk the stalk to calculate the germinating roots for. * @param clearOdd whether to clear the odd or even germinating stalk. */ function claimGerminatingRoots( address account, uint32 season, uint128 stalk, bool clearOdd ) private returns (uint128 roots, Germinate germState) { AppStorage storage s = LibAppStorage.diamondStorage(); roots = calculateGerminatingRoots(season, stalk); if (clearOdd) { delete s.a[account].farmerGerminating.odd; germState = Germinate.ODD; } else { delete s.a[account].farmerGerminating.even; germState = Germinate.EVEN; } // deduct from unclaimed values. s.unclaimedGerminating[season].stalk = s.unclaimedGerminating[season].stalk.sub(stalk); s.unclaimedGerminating[season].roots = s.unclaimedGerminating[season].roots.sub(roots); } /** * @notice calculates the germinating roots for a given stalk and season. * @param season the season to use when calculating the germinating roots. * @param stalk the stalk to calculate the germinating roots for. */ function calculateGerminatingRoots( uint32 season, uint128 stalk ) private view returns (uint128 roots) { AppStorage storage s = LibAppStorage.diamondStorage(); // if the stalk is equal to the remaining unclaimed germinating stalk, // then return the remaining unclaimed germinating roots. if (stalk == s.unclaimedGerminating[season].stalk) { roots = s.unclaimedGerminating[season].roots; } else { // calculate the roots. casted up to uint256 to prevent overflow, // and safecasted down. roots = uint256(stalk).mul(s.unclaimedGerminating[season].roots).div( s.unclaimedGerminating[season].stalk ).toUint128(); } } /** * @notice returns the germinatingStalk of the account, * ordered based on the parity of lastUpdate. * @dev if lastUpdate is odd, then `firstStalk` is the odd stalk. */ function getGerminatingStalk( address account, bool lastUpdateOdd ) internal view returns (uint128 firstStalk, uint128 secondStalk) { AppStorage storage s = LibAppStorage.diamondStorage(); if (lastUpdateOdd) { firstStalk = s.a[account].farmerGerminating.odd; secondStalk = s.a[account].farmerGerminating.even; } else { firstStalk = s.a[account].farmerGerminating.even; secondStalk = s.a[account].farmerGerminating.odd; } } /** * @notice returns the germinating stalk and roots that will finish germinating * upon an interaction with the silo. */ function getFinishedGerminatingStalkAndRoots( address account, uint32 lastMowedSeason, uint32 currentSeason ) internal view returns (uint256 germinatingStalk, uint256 germinatingRoots) { // if user has mowed already, // then there are no germinating stalk and roots to finish. if (lastMowedSeason == currentSeason) { return (0, 0); } (uint128 firstStalk, uint128 secondStalk) = getGerminatingStalk( account, isSeasonOdd(lastMowedSeason) ); // check to end germination for first stalk. // if last mowed season is the greater or equal than (currentSeason - 1), // then the first stalk is still germinating. if (firstStalk > 0 && lastMowedSeason < currentSeason.sub(1)) { germinatingStalk = firstStalk; germinatingRoots = calculateGerminatingRoots(lastMowedSeason, firstStalk); } // check to end germination for second stalk. if (secondStalk > 0) { germinatingStalk = germinatingStalk.add(secondStalk); germinatingRoots = germinatingRoots.add( calculateGerminatingRoots(lastMowedSeason.sub(1), secondStalk) ); } } /** * @notice returns the stalk currently germinating for an account. * Does not include germinating stalk that will finish germinating * upon an interaction with the silo. */ function getCurrentGerminatingStalk( address account, uint32 lastMowedSeason ) internal view returns (uint256 germinatingStalk) { AppStorage storage s = LibAppStorage.diamondStorage(); // if the last mowed season is less than the current season - 1, // then there are no germinating stalk and roots (as all germinating assets have finished). if (lastMowedSeason < s.season.current.sub(1)) { return 0; } else { (uint128 firstStalk, uint128 secondStalk) = getGerminatingStalk( account, isSeasonOdd(lastMowedSeason) ); germinatingStalk = firstStalk.add(secondStalk); } } /** * @notice returns the unclaimed germinating stalk and roots. */ function getUnclaimedGerminatingStalkAndRoots( uint32 season ) internal view returns (Storage.Sr memory unclaimed) { AppStorage storage s = LibAppStorage.diamondStorage(); unclaimed = s.unclaimedGerminating[season]; } /** * @notice returns the total germinating bdv and amount for a token. */ function getTotalGerminatingForToken( address token ) internal view returns (uint256 bdv, uint256 amount) { AppStorage storage s = LibAppStorage.diamondStorage(); return ( s.oddGerminating.deposited[token].bdv.add(s.evenGerminating.deposited[token].bdv), s.oddGerminating.deposited[token].amount.add(s.evenGerminating.deposited[token].amount) ); } /** * @notice determines whether a deposit (token + stem) should be germinating or not. * If germinating, determines whether the deposit should be set to even or odd. * * @dev `getGerminationState` should be used if the stemTip and germinatingStem * have not been calculated yet. Otherwise, use `_getGerminationState` for gas effiecnecy. */ function getGerminationState( address token, int96 stem ) internal view returns (Germinate, int96) { GermStem memory germStem = getGerminatingStem(token); return (_getGerminationState(stem, germStem), germStem.stemTip); } /** * @notice returns the `germinating` stem of a token. * @dev the 'germinating' stem is the stem where deposits that have a stem * equal or higher than this value are germinating. */ function getGerminatingStem(address token) internal view returns (GermStem memory germStem) { germStem.stemTip = LibTokenSilo.stemTipForToken(token); germStem.germinatingStem = _getGerminatingStem(token, germStem.stemTip); } /** * @notice returns the `germinating` stem of a token. * @dev the 'germinating' stem is the stem where deposits that have a stem * equal or higher than this value are germinating. */ function _getGerminatingStem(address token, int96 stemTip) internal view returns (int96 stem) { return __getGerminatingStem(stemTip, getPrevStalkEarnedPerSeason(token)); } /** * @notice Gas efficent version of `_getGerminatingStem`. * * @dev use when the stemTip and germinatingStem have already been calculated. * Assumes the same token is used. * prevStalkEarnedPerSeason is the stalkEarnedPerSeason of the previous season. * since `lastStemTip` + `prevStalkEarnedPerSeason` is the current stemTip, * safeMath is not needed. */ function __getGerminatingStem( int96 stemTip, int96 prevStalkEarnedPerSeason ) internal pure returns (int96 stem) { return stemTip - prevStalkEarnedPerSeason; } /** * @notice returns the stalkEarnedPerSeason of a token of the previous season. * @dev if the milestone season is not the current season, then the stalkEarnedPerSeason * hasn't changed from the previous season. Otherwise, we calculate the prevStalkEarnedPerSeason. */ function getPrevStalkEarnedPerSeason( address token ) private view returns (uint32 prevStalkEarnedPerSeason) { AppStorage storage s = LibAppStorage.diamondStorage(); if (s.ss[token].milestoneSeason < s.season.current) { prevStalkEarnedPerSeason = s.ss[token].stalkEarnedPerSeason; } else { int24 deltaStalkEarnedPerSeason = s.ss[token].deltaStalkEarnedPerSeason; if (deltaStalkEarnedPerSeason >= 0) { prevStalkEarnedPerSeason = s.ss[token].stalkEarnedPerSeason - uint32(deltaStalkEarnedPerSeason); } else { prevStalkEarnedPerSeason = s.ss[token].stalkEarnedPerSeason + uint32(-deltaStalkEarnedPerSeason); } } } /** * @notice internal function for germination stem. * @dev a deposit is germinating if the stem is the stemTip or the germinationStem. * the 'germinationStem` is the stem of the token of the previous season. * * The function must check whether the stem is equal to the germinationStem, * to determine which germination state it is in. */ function _getGerminationState( int96 stem, GermStem memory germData ) internal view returns (Germinate) { if (stem < germData.germinatingStem) { // if the stem of the deposit is lower than the germination stem, // then the deposit is not germinating. return Germinate.NOT_GERMINATING; } else { // return the gemination state based on whether the stem // is equal to the stemTip. // if the stem is equal to the stem tip, it is in the initial stages of germination. // if the stem is not equal to the stemTip, its in the germination process. if (stem == germData.stemTip) { return isCurrentSeasonOdd() ? Germinate.ODD : Germinate.EVEN; } else { return isCurrentSeasonOdd() ? Germinate.EVEN : Germinate.ODD; } } } /** * @notice returns the germination state for the current season. * @dev used in new deposits, as all new deposits are germinating. */ function getSeasonGerminationState() internal view returns (Germinate) { AppStorage storage s = LibAppStorage.diamondStorage(); return getGerminationStateForSeason(s.season.current); } /** * @notice returns the germination state for a given season. */ function getGerminationStateForSeason(uint32 season) internal pure returns (Germinate) { return isSeasonOdd(season) ? Germinate.ODD : Germinate.EVEN; } /** * @notice returns whether the current season is odd. Used for Germination. * @dev even % 2 = 0 (false), odd % 2 = 1 (true) */ function isCurrentSeasonOdd() internal view returns (bool) { AppStorage storage s = LibAppStorage.diamondStorage(); return isSeasonOdd(s.season.current); } /** * @notice returns whether `season` is odd. */ function isSeasonOdd(uint32 season) internal pure returns (bool) { return season.mod(2) == 0 ? false : true; } }
/** * SPDX-License-Identifier: MIT **/ pragma solidity =0.7.6; pragma abicoder v2; import "../LibAppStorage.sol"; import {C} from "../../C.sol"; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/SafeCast.sol"; import {LibBytes} from "../LibBytes.sol"; import {LibTokenSilo} from "./LibTokenSilo.sol"; import {LibSafeMath128} from "../LibSafeMath128.sol"; import {LibSafeMath32} from "../LibSafeMath32.sol"; import {LibSafeMathSigned96} from "../LibSafeMathSigned96.sol"; import {LibGerminate} from "./LibGerminate.sol"; import {LibWhitelistedTokens} from "./LibWhitelistedTokens.sol"; /** * @title LibSilo * @author Publius * @notice Contains functions for minting, burning, and transferring of * Stalk and Roots within the Silo. * * @dev Here, we refer to "minting" as the combination of * increasing the total balance of Stalk/Roots, as well as allocating * them to a particular account. However, in other places throughout Beanstalk * (like during the Sunrise), Beanstalk's total balance of Stalk increases * without allocating to a particular account. One example is {Sun-rewardToSilo} * which increases `s.s.stalk` but does not allocate it to any account. The * allocation occurs during `{SiloFacet-plant}`. Does this change how we should * call "minting"? * * In the ERC20 context, "minting" increases the supply of a token and allocates * the new tokens to an account in one action. I've adjusted the comments below * to use "mint" in the same sense. */ library LibSilo { using SafeMath for uint256; using LibSafeMath128 for uint128; using LibSafeMathSigned96 for int96; using LibSafeMath32 for uint32; using SafeCast for uint256; //////////////////////// ENUM //////////////////////// /** * @dev when a user removes multiple deposits, the * {TransferBatch} event is emitted. However, in the * case of an enroot, the event is omitted (as the * depositID does not change). This enum is * used to determine if the event should be emitted. */ enum ERC1155Event { EMIT_BATCH_EVENT, NO_EMIT_BATCH_EVENT } uint128 internal constant PRECISION = 1e6; //////////////////////// EVENTS //////////////////////// /** * @notice Emitted when `account` gains or loses Stalk. * @param account The account that gained or lost Stalk. * @param delta The change in Stalk. * @param deltaRoots The change in Roots. * * @dev Should be emitted anytime a Deposit is added, removed or transferred * AND anytime an account Mows Grown Stalk. * * BIP-24 included a one-time re-emission of {StalkBalanceChanged} for * accounts that had executed a Deposit transfer between the Replant and * BIP-24 execution. For more, see: * * [BIP-24](https://bean.money/bip-24) * [Event-Emission](https://github.com/BeanstalkFarms/BIP-24-Event-Emission) */ event StalkBalanceChanged(address indexed account, int256 delta, int256 deltaRoots); /** * @notice Emitted when a deposit is removed from the silo. * * @param account The account assoicated with the removed deposit. * @param token The token address of the removed deposit. * @param stem The stem of the removed deposit. * @param amount The amount of "token" removed from an deposit. * @param bdv The instanteous bdv removed from the deposit. */ event RemoveDeposit( address indexed account, address indexed token, int96 stem, uint256 amount, uint256 bdv ); /** * @notice Emitted when multiple deposits are removed from the silo. * * @param account The account assoicated with the removed deposit. * @param token The token address of the removed deposit. * @param stems A list of stems of the removed deposits. * @param amounts A list of amounts removed from the deposits. * @param amount the total summation of the amount removed. * @param bdvs A list of bdvs removed from the deposits. */ event RemoveDeposits( address indexed account, address indexed token, int96[] stems, uint256[] amounts, uint256 amount, uint256[] bdvs ); /** * AssetsRemoved contains the assets removed * during a withdraw or convert. * * @dev seperated into 3 catagories: * active: non-germinating assets. * odd: odd germinating assets. * even: even germinating assets. * grownStalk from germinating depoists are seperated * as that stalk is not germinating. */ struct AssetsRemoved { Removed active; Removed odd; Removed even; uint256 grownStalkFromGermDeposits; } struct Removed { uint256 tokens; uint256 stalk; uint256 bdv; } /** * @notice Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all * transfers. */ event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values ); //////////////////////// MINT //////////////////////// /** * @dev Mints Stalk and Roots to `account`. * * `roots` are an underlying accounting variable that is used to track * how many earned beans a user has. * * When a farmer's state is updated, the ratio should hold: * * Total Roots User Roots * ------------- = ------------ * Total Stalk User Stalk * * @param account the address to mint Stalk and Roots to * @param stalk the amount of stalk to mint * * @dev Stalk that is not germinating are `active`, meaning that they * are eligible for bean mints. To mint germinating stalk, use * `mintGerminatingStalk`. */ function mintActiveStalk(address account, uint256 stalk) internal { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 roots; if (s.s.roots == 0) { roots = uint256(stalk.mul(C.getRootsBase())); } else { // germinating assets should be considered // when calculating roots roots = s.s.roots.mul(stalk).div(s.s.stalk); } // increment user and total stalk; s.s.stalk = s.s.stalk.add(stalk); s.a[account].s.stalk = s.a[account].s.stalk.add(stalk); // increment user and total roots s.s.roots = s.s.roots.add(roots); s.a[account].roots = s.a[account].roots.add(roots); emit StalkBalanceChanged(account, int256(stalk), int256(roots)); } /** * @notice mintGerminatingStalk contains logic for minting stalk that is germinating. * @dev `germinating stalk` are newly issued stalk that are not eligible for bean mints, * until 2 `gm` calls have passed, at which point they are considered `grown stalk`. * * Since germinating stalk are not elgible for bean mints, when calculating the roots of these * stalk, it should use the stalk and roots of the system once the stalk is fully germinated, * rather than at the time of minting. */ function mintGerminatingStalk( address account, uint128 stalk, LibGerminate.Germinate germ ) internal { AppStorage storage s = LibAppStorage.diamondStorage(); if (germ == LibGerminate.Germinate.ODD) { s.a[account].farmerGerminating.odd = s.a[account].farmerGerminating.odd.add(stalk); } else { s.a[account].farmerGerminating.even = s.a[account].farmerGerminating.even.add(stalk); } // germinating stalk are either newly germinating, or partially germinated. // Thus they can only be incremented in the latest or previous season. uint32 germinationSeason = s.season.current; if (LibGerminate.getSeasonGerminationState() == germ) { s.unclaimedGerminating[germinationSeason].stalk = s.unclaimedGerminating[germinationSeason].stalk.add(stalk); } else { germinationSeason = germinationSeason.sub(1); s.unclaimedGerminating[germinationSeason].stalk = s.unclaimedGerminating[germinationSeason].stalk .add(stalk); } // emit events. emit LibGerminate.FarmerGerminatingStalkBalanceChanged(account, stalk, germ); emit LibGerminate.TotalGerminatingStalkChanged(germinationSeason, stalk); } //////////////////////// BURN //////////////////////// /** * @notice Burns stalk and roots from an account. */ function burnActiveStalk(address account, uint256 stalk) internal returns (uint256 roots) { AppStorage storage s = LibAppStorage.diamondStorage(); if (stalk == 0) return 0; // Calculate the amount of Roots for the given amount of Stalk. roots = s.s.roots.mul(stalk).div(s.s.stalk); if (roots > s.a[account].roots) roots = s.a[account].roots; // Decrease supply of Stalk; Remove Stalk from the balance of `account` s.s.stalk = s.s.stalk.sub(stalk); s.a[account].s.stalk = s.a[account].s.stalk.sub(stalk); // Decrease supply of Roots; Remove Roots from the balance of `account` s.s.roots = s.s.roots.sub(roots); s.a[account].roots = s.a[account].roots.sub(roots); // If it is Raining and the account now has less roots than the // account's current rain roots, set the account's rain roots // to the account's current roots and subtract the difference // from Beanstalk's total rain roots. if (s.a[account].sop.roots > s.a[account].roots) { uint256 deltaRoots = s.a[account].sop.roots.sub(s.a[account].roots); s.a[account].sop.roots = s.a[account].roots; s.r.roots = s.r.roots.sub(deltaRoots); } // emit event. emit StalkBalanceChanged(account, -int256(stalk), -int256(roots)); } /** * @notice Burns germinating stalk. * @dev Germinating stalk does not have any roots assoicated with it. */ function burnGerminatingStalk( address account, uint128 stalk, LibGerminate.Germinate germ ) external { AppStorage storage s = LibAppStorage.diamondStorage(); if (germ == LibGerminate.Germinate.ODD) { s.a[account].farmerGerminating.odd = s.a[account].farmerGerminating.odd.sub(stalk); } else { s.a[account].farmerGerminating.even = s.a[account].farmerGerminating.even.sub(stalk); } // germinating stalk are either newly germinating, or partially germinated. // Thus they can only be decremented in the latest or previous season. uint32 germinationSeason = s.season.current; if (LibGerminate.getSeasonGerminationState() == germ) { s.unclaimedGerminating[germinationSeason].stalk = s.unclaimedGerminating[germinationSeason].stalk.sub(stalk); } else { germinationSeason = germinationSeason.sub(1); s.unclaimedGerminating[germinationSeason].stalk = s.unclaimedGerminating[germinationSeason].stalk .sub(stalk); } // emit events. emit LibGerminate.FarmerGerminatingStalkBalanceChanged(account, -int256(stalk), germ); emit LibGerminate.TotalGerminatingStalkChanged(germinationSeason, -int256(stalk)); } //////////////////////// TRANSFER //////////////////////// /** * @notice Decrements the Stalk and Roots of `sender` and increments the Stalk * and Roots of `recipient` by the same amount. * */ function transferStalk(address sender, address recipient, uint256 stalk) internal { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 roots; roots = stalk == s.a[sender].s.stalk ? s.a[sender].roots : s.s.roots.sub(1).mul(stalk).div(s.s.stalk).add(1); // Subtract Stalk and Roots from the 'sender' balance. s.a[sender].s.stalk = s.a[sender].s.stalk.sub(stalk); s.a[sender].roots = s.a[sender].roots.sub(roots); emit StalkBalanceChanged(sender, -int256(stalk), -int256(roots)); // Rain roots cannot be transferred, burn them if (s.a[sender].sop.roots > s.a[sender].roots) { uint256 deltaRoots = s.a[sender].sop.roots.sub(s.a[sender].roots); s.a[sender].sop.roots = s.a[sender].roots; s.r.roots = s.r.roots.sub(deltaRoots); } // Add Stalk and Roots to the 'recipient' balance. s.a[recipient].s.stalk = s.a[recipient].s.stalk.add(stalk); s.a[recipient].roots = s.a[recipient].roots.add(roots); emit StalkBalanceChanged(recipient, int256(stalk), int256(roots)); } /** * @notice germinating counterpart of `transferStalk`. * @dev assumes stalk is germinating. */ function transferGerminatingStalk( address sender, address recipient, uint256 stalk, LibGerminate.Germinate germState ) internal { AppStorage storage s = LibAppStorage.diamondStorage(); // Subtract Germinating Stalk from the 'sender' balance, // and Add to the 'recipient' balance. if (germState == LibGerminate.Germinate.ODD) { s.a[sender].farmerGerminating.odd = s.a[sender].farmerGerminating.odd.sub(stalk.toUint128()); s.a[recipient].farmerGerminating.odd = s.a[recipient].farmerGerminating.odd.add(stalk.toUint128()); } else { s.a[sender].farmerGerminating.even = s.a[sender].farmerGerminating.even.sub(stalk.toUint128()); s.a[recipient].farmerGerminating.even = s.a[recipient].farmerGerminating.even.add(stalk.toUint128()); } // emit events. emit LibGerminate.FarmerGerminatingStalkBalanceChanged(sender, -int256(stalk), germState); emit LibGerminate.FarmerGerminatingStalkBalanceChanged(recipient, int256(stalk), germState); } /** * @notice transfers both stalk and Germinating Stalk. * @dev used in {TokenSilo._transferDeposits} */ function transferStalkAndGerminatingStalk( address sender, address recipient, address token, AssetsRemoved memory ar ) external { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 stalkPerBDV = s.ss[token].stalkIssuedPerBdv; if (ar.odd.bdv > 0) { uint256 initialStalk = ar.odd.bdv.mul(stalkPerBDV); if (token == C.BEAN) { // check whether the Germinating Stalk transferred exceeds the farmers // Germinating Stalk. If so, the difference is considered from Earned // Beans. Deduct the odd BDV and increment the activeBDV by the difference. (uint256 senderGerminatingStalk, uint256 earnedBeansStalk) = checkForEarnedBeans( sender, initialStalk, LibGerminate.Germinate.ODD ); if (earnedBeansStalk > 0) { // increment the active stalk by the earned beans active stalk. // decrement the germinatingStalk stalk by the earned beans active stalk. ar.active.stalk = ar.active.stalk.add(earnedBeansStalk); initialStalk = senderGerminatingStalk; } } // the inital Stalk issued for a Deposit is the // only Stalk that can Germinate (i.e, Grown Stalk does not Germinate). transferGerminatingStalk( sender, recipient, initialStalk, LibGerminate.Germinate.ODD ); } if (ar.even.bdv > 0) { uint256 initialStalk = ar.even.bdv.mul(stalkPerBDV); if (token == C.BEAN) { // check whether the Germinating Stalk transferred exceeds the farmers // Germinating Stalk. If so, the difference is considered from Earned // Beans. Deduct the even BDV and increment the active BDV by the difference. (uint256 senderGerminatingStalk, uint256 earnedBeansStalk) = checkForEarnedBeans( sender, initialStalk, LibGerminate.Germinate.EVEN ); if (earnedBeansStalk > 0) { // increment the active stalk by the earned beans active stalk. // decrement the germinatingStalk stalk by the earned beans active stalk. ar.active.stalk = ar.active.stalk.add(earnedBeansStalk); initialStalk = senderGerminatingStalk; } } // the inital Stalk issued for a Deposit is the // only Stalk that can Germinate (i.e, Grown Stalk does not Germinate). transferGerminatingStalk( sender, recipient, initialStalk, LibGerminate.Germinate.EVEN ); } // a Germinating Deposit may have Grown Stalk (which is not Germinating), // but the base Stalk is still Germinating. ar.active.stalk = ar.active.stalk // Grown Stalk from non-Germinating Deposits, and base stalk from Earned Bean Deposits. .add(ar.active.bdv.mul(stalkPerBDV)) // base stalk from non-germinating deposits. .add(ar.even.stalk) // grown stalk from Even Germinating Deposits. .add(ar.odd.stalk); // grown stalk from Odd Germinating Deposits. if (ar.active.stalk > 0) { transferStalk(sender, recipient, ar.active.stalk); } } /** * @dev Claims the Grown Stalk for `account` and applies it to their Stalk * balance. Also handles Season of Plenty related rain. * * This is why `_mow()` must be called before any actions that change Seeds, * including: * - {SiloFacet-deposit} * - {SiloFacet-withdrawDeposit} * - {SiloFacet-withdrawDeposits} * - {_plant} * - {SiloFacet-transferDeposit(s)} */ function _mow(address account, address token) internal { AppStorage storage s = LibAppStorage.diamondStorage(); // if the user has not migrated from siloV2, revert. (bool needsMigration, uint32 lastUpdate) = migrationNeeded(account); require(!needsMigration, "Silo: Migration needed"); // if the user hasn't updated prior to the seedGauge/siloV3.1 update, // perform a one time `lastStem` scale. if ( (lastUpdate < s.season.stemScaleSeason && lastUpdate > 0) || (lastUpdate == s.season.stemScaleSeason && checkStemEdgeCase(account)) ) { migrateStems(account); } uint32 currentSeason = s.season.current; // End account germination. uint128 firstGerminatingRoots; if (lastUpdate < currentSeason) { firstGerminatingRoots = LibGerminate.endAccountGermination(account, lastUpdate, currentSeason); } // sop data only needs to be updated once per season, // if it started raining and it's still raining, or there was a sop if (s.season.rainStart > s.season.stemStartSeason) { if ((lastUpdate <= s.season.rainStart || s.a[account].lastRain > 0) && lastUpdate <= currentSeason) { // Increments `plenty` for `account` if a Flood has occured. // Saves Rain Roots for `account` if it is Raining. handleRainAndSops(account, lastUpdate, firstGerminatingRoots); } } // Calculate the amount of Grown Stalk claimable by `account`. // Increase the account's balance of Stalk and Roots. __mow(account, token); // update lastUpdate for sop and germination calculations. s.a[account].lastUpdate = currentSeason; } /** * @dev Updates the mowStatus for the given account and token, * and mints Grown Stalk for the given account and token. */ function __mow( address account, address token ) private { AppStorage storage s = LibAppStorage.diamondStorage(); int96 _stemTip = LibTokenSilo.stemTipForToken(token); int96 _lastStem = s.a[account].mowStatuses[token].lastStem; uint128 _bdv = s.a[account].mowStatuses[token].bdv; // if: // 1: account has no bdv (new token deposit) // 2: the lastStem is the same as the stemTip (implying that a user has mowed), // then skip calculations to save gas. if (_bdv > 0) { if (_lastStem == _stemTip) { return; } // grown stalk does not germinate and is immediately included for bean mints. mintActiveStalk(account, _balanceOfGrownStalk(_lastStem, _stemTip, _bdv)); } // If this `account` has no BDV, skip to save gas. Update lastStem. // (happen on initial deposit, since mow is called before any deposit) s.a[account].mowStatuses[token].lastStem = _stemTip; return; } /** * @notice returns the last season an account interacted with the silo. */ function _lastUpdate(address account) internal view returns (uint32) { AppStorage storage s = LibAppStorage.diamondStorage(); return s.a[account].lastUpdate; } /** * @dev internal logic to handle when beanstalk is raining. */ function handleRainAndSops(address account, uint32 lastUpdate, uint128 firstGerminatingRoots) private { AppStorage storage s = LibAppStorage.diamondStorage(); // If a Sop has occured since last update, calculate rewards and set last Sop. if (s.season.lastSopSeason > lastUpdate) { s.a[account].sop.plenty = balanceOfPlenty(account); s.a[account].lastSop = s.season.lastSop; } // If no roots, reset Sop counters variables if (s.a[account].roots == 0) { s.a[account].lastSop = s.season.rainStart; s.a[account].lastRain = 0; return; } if (s.season.raining) { // If rain started after update, set account variables to track rain. if (s.season.rainStart > lastUpdate) { s.a[account].lastRain = s.season.rainStart; s.a[account].sop.roots = s.a[account].roots; if (s.season.rainStart - 1 == lastUpdate) { if (firstGerminatingRoots > 0) { // if the account had just finished germinating roots this season (i.e, // deposited in the previous season before raining, and mowed during a sop), // Beanstalk will not allocate plenty to this deposit, and thus the roots // needs to be deducted from the sop roots. s.a[account].sop.roots = s.a[account].sop.roots.sub(firstGerminatingRoots); } } // s.a[account].roots includes newly finished germinating roots from a fresh deposit // @ season before rainStart } // If there has been a Sop since rain started, // save plentyPerRoot in case another SOP happens during rain. if (s.season.lastSop == s.season.rainStart) { s.a[account].sop.plentyPerRoot = s.sops[s.season.lastSop]; } } else if (s.a[account].lastRain > 0) { // Reset Last Rain if not raining. s.a[account].lastRain = 0; } } /** * @dev returns the balance of amount of grown stalk based on stems. * @param lastStem the stem assoicated with the last mow * @param latestStem the current stem for a given token * @param bdv the bdv used to calculate grown stalk */ function _balanceOfGrownStalk( int96 lastStem, int96 latestStem, uint128 bdv ) internal pure returns (uint256) { return stalkReward(lastStem, latestStem, bdv); } /** * @dev returns the amount of `plenty` an account has. */ function balanceOfPlenty(address account) internal view returns (uint256 plenty) { AppStorage storage s = LibAppStorage.diamondStorage(); Account.State storage a = s.a[account]; plenty = a.sop.plenty; uint256 previousPPR; // If lastRain > 0, then check if SOP occured during the rain period. if (s.a[account].lastRain > 0) { // if the last processed SOP = the lastRain processed season, // then we use the stored roots to get the delta. if (a.lastSop == a.lastRain) previousPPR = a.sop.plentyPerRoot; else previousPPR = s.sops[a.lastSop]; uint256 lastRainPPR = s.sops[s.a[account].lastRain]; // If there has been a SOP duing the rain sesssion since last update, process SOP. if (lastRainPPR > previousPPR) { uint256 plentyPerRoot = lastRainPPR - previousPPR; previousPPR = lastRainPPR; plenty = plenty.add(plentyPerRoot.mul(s.a[account].sop.roots).div(C.SOP_PRECISION)); } } else { // If it was not raining, just use the PPR at previous SOP. previousPPR = s.sops[s.a[account].lastSop]; } // Handle and SOPs that started + ended before after last Silo update. if (s.season.lastSop > _lastUpdate(account)) { uint256 plentyPerRoot = s.sops[s.season.lastSop].sub(previousPPR); plenty = plenty.add(plentyPerRoot.mul(s.a[account].roots).div(C.SOP_PRECISION)); } } //////////////////////// REMOVE //////////////////////// /** * @dev Removes from a single Deposit, emits the RemoveDeposit event, * and returns the Stalk/BDV that were removed. * * Used in: * - {TokenSilo:_withdrawDeposit} * - {TokenSilo:_transferDeposit} */ function _removeDepositFromAccount( address account, address token, int96 stem, uint256 amount, LibTokenSilo.Transfer transferType ) internal returns ( uint256 initialStalkRemoved, uint256 grownStalkRemoved, uint256 bdvRemoved, LibGerminate.Germinate germ ) { AppStorage storage s = LibAppStorage.diamondStorage(); int96 stemTip; (germ, stemTip) = LibGerminate.getGerminationState(token, stem); bdvRemoved = LibTokenSilo.removeDepositFromAccount(account, token, stem, amount); // the initial and grown stalk are seperated as there are instances // where the initial stalk issued for a deposit is germinating. Grown stalk never germinates, // and thus is not included in the germinating stalk. initialStalkRemoved = bdvRemoved.mul(s.ss[token].stalkIssuedPerBdv); grownStalkRemoved = stalkReward(stem, stemTip, bdvRemoved.toUint128()); /** * {_removeDepositFromAccount} is used for both withdrawing and transferring deposits. * In the case of a withdraw, only the {TransferSingle} Event needs to be emitted. * In the case of a transfer, a different {TransferSingle}/{TransferBatch} * Event is emitted in {TokenSilo._transferDeposit(s)}, * and thus, this event is ommited. */ if (transferType == LibTokenSilo.Transfer.emitTransferSingle) { // "removing" a deposit is equivalent to "burning" an ERC1155 token. emit LibTokenSilo.TransferSingle( msg.sender, // operator account, // from address(0), // to LibBytes.packAddressAndStem(token, stem), // depositid amount // token amount ); } emit RemoveDeposit(account, token, stem, amount, bdvRemoved); } /** * @dev Removes from multiple Deposits, emits the RemoveDeposits * event, and returns the Stalk/BDV that were removed. * * Used in: * - {TokenSilo:_withdrawDeposits} * - {SiloFacet:enrootDeposits} * * @notice with the addition of germination, AssetsRemoved * keeps track of the germinating data. */ function _removeDepositsFromAccount( address account, address token, int96[] calldata stems, uint256[] calldata amounts, ERC1155Event emission ) external returns (AssetsRemoved memory ar) { AppStorage storage s = LibAppStorage.diamondStorage(); uint256[] memory bdvsRemoved = new uint256[](stems.length); uint256[] memory removedDepositIDs = new uint256[](stems.length); LibGerminate.GermStem memory germStem = LibGerminate.getGerminatingStem(token); for (uint256 i; i < stems.length; ++i) { LibGerminate.Germinate germState = LibGerminate._getGerminationState( stems[i], germStem ); uint256 crateBdv = LibTokenSilo.removeDepositFromAccount( account, token, stems[i], amounts[i] ); bdvsRemoved[i] = crateBdv; removedDepositIDs[i] = LibBytes.packAddressAndStem(token, stems[i]); uint256 crateStalk = stalkReward(stems[i], germStem.stemTip, crateBdv.toUint128()); // if the deposit is germinating, decrement germinating values, // otherwise increment deposited values. if (germState == LibGerminate.Germinate.NOT_GERMINATING) { ar.active.bdv = ar.active.bdv.add(crateBdv); ar.active.stalk = ar.active.stalk.add(crateStalk); ar.active.tokens = ar.active.tokens.add(amounts[i]); } else { if (germState == LibGerminate.Germinate.ODD) { ar.odd.bdv = ar.odd.bdv.add(crateBdv); ar.odd.tokens = ar.odd.tokens.add(amounts[i]); } else { ar.even.bdv = ar.even.bdv.add(crateBdv); ar.even.tokens = ar.even.tokens.add(amounts[i]); } // grown stalk from germinating deposits do not germinate, // and thus must be added to the grown stalk. ar.grownStalkFromGermDeposits = ar.grownStalkFromGermDeposits.add( crateStalk ); } } // add initial stalk deposit to all stalk removed. { uint256 stalkIssuedPerBdv = s.ss[token].stalkIssuedPerBdv; if (ar.active.tokens > 0) { ar.active.stalk = ar.active.stalk.add(ar.active.bdv.mul(stalkIssuedPerBdv)); } if (ar.odd.tokens > 0) { ar.odd.stalk = ar.odd.bdv.mul(stalkIssuedPerBdv); } if (ar.even.tokens > 0) { ar.even.stalk = ar.even.bdv.mul(stalkIssuedPerBdv); } } // "removing" deposits is equivalent to "burning" a batch of ERC1155 tokens. if (emission == ERC1155Event.EMIT_BATCH_EVENT) { emit TransferBatch(msg.sender, account, address(0), removedDepositIDs, amounts); } emit RemoveDeposits( account, token, stems, amounts, ar.active.tokens.add(ar.odd.tokens).add(ar.even.tokens), bdvsRemoved ); } //////////////////////// UTILITIES //////////////////////// /** * @notice Calculates the Stalk reward based on the start and end * stems, and the amount of BDV deposited. Stems represent the * amount of grown stalk per BDV, so the difference between the * start index and end index (stem) multiplied by the amount of * bdv deposited will give the amount of stalk earned. * formula: stalk = bdv * (ΔstalkPerBdv) * * @dev endStem must be larger than startStem. * */ function stalkReward( int96 startStem, int96 endStem, uint128 bdv ) internal pure returns (uint256) { uint128 reward = uint128(endStem.sub(startStem)).mul(bdv).div(PRECISION); return reward; } /** * @dev check whether the account needs to be migrated. */ function migrationNeeded(address account) internal view returns (bool needsMigration, uint32 lastUpdate) { AppStorage storage s = LibAppStorage.diamondStorage(); lastUpdate = s.a[account].lastUpdate; needsMigration = lastUpdate > 0 && lastUpdate < s.season.stemStartSeason; } /** * @dev Internal function to compute `account` balance of Earned Beans. * * The number of Earned Beans is equal to the difference between: * - the "expected" Stalk balance, determined from the account balance of * Roots. * - the "account" Stalk balance, stored in account storage. * divided by the number of Stalk per Bean. * The earned beans from the latest season */ function _balanceOfEarnedBeans( uint256 accountStalk, uint256 accountRoots ) internal view returns (uint256 beans) { AppStorage storage s = LibAppStorage.diamondStorage(); // There will be no Roots before the first Deposit is made. if (s.s.roots == 0) return 0; uint256 stalk = s.s.stalk.mul(accountRoots).div(s.s.roots); // Beanstalk rounds down when minting Roots. Thus, it is possible that // balanceOfRoots / totalRoots * totalStalk < s.a[account].s.stalk. // As `account` Earned Balance balance should never be negative, // Beanstalk returns 0 instead. if (stalk <= accountStalk) return 0; // Calculate Earned Stalk and convert to Earned Beans. beans = (stalk - accountStalk).div(C.STALK_PER_BEAN); // Note: SafeMath is redundant here. if (beans > s.earnedBeans) return s.earnedBeans; return beans; } /** * @notice performs a one time update for the * users lastStem for all silo Tokens. * @dev Due to siloV3.1 update. */ function migrateStems(address account) internal { AppStorage storage s = LibAppStorage.diamondStorage(); address[] memory siloTokens = LibWhitelistedTokens.getSiloTokens(); for(uint i; i < siloTokens.length; i++) { // scale lastStem by 1e6, if the user has a lastStem. if (s.a[account].mowStatuses[siloTokens[i]].lastStem > 0) { s.a[account].mowStatuses[siloTokens[i]].lastStem = s.a[account].mowStatuses[siloTokens[i]].lastStem.mul(int96(PRECISION)); } } } /** * @dev An edge case can occur with the siloV3.1 update, where * A user updates their silo in the same season as the seedGauge update, * but prior to the seedGauge BIP execution (i.e the farmer mowed at the start of * the season, and the BIP was excuted mid-way through the season). * This function checks for that edge case and returns a boolean. */ function checkStemEdgeCase(address account) internal view returns (bool) { AppStorage storage s = LibAppStorage.diamondStorage(); address[] memory siloTokens = LibWhitelistedTokens.getSiloTokens(); // for each silo token, divide the stemTip of the token with the users last stem. // if the answer is 1e6 or greater, the user has not updated. for(uint i; i < siloTokens.length; i++) { int96 lastStem = s.a[account].mowStatuses[siloTokens[i]].lastStem; if (lastStem > 0) { if (LibTokenSilo.stemTipForToken(siloTokens[i]).div(lastStem) >= int96(PRECISION)) { return true; } } } return false; } /** * @notice Returns the amount of Germinating Stalk * for a given Germinate enum. * @dev When a Farmer attempts to withdraw Beans from a Deposit that has a Germinating Stem, * `checkForEarnedBeans` is called to determine how many of the Beans were Planted vs Deposited. * If a Farmer withdraws a Germinating Deposit with Earned Beans, only subtract the Germinating Beans * from the Germinating Balances * @return germinatingStalk stalk that is germinating for a given Germinate enum. * @return earnedBeanStalk the earned bean portion of stalk for a given Germinate enum. */ function checkForEarnedBeans( address account, uint256 stalk, LibGerminate.Germinate germ ) internal view returns (uint256 germinatingStalk, uint256 earnedBeanStalk) { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 farmerGerminatingStalk; if (germ == LibGerminate.Germinate.ODD) { farmerGerminatingStalk = s.a[account].farmerGerminating.odd; } else { farmerGerminatingStalk = s.a[account].farmerGerminating.even; } if (stalk > farmerGerminatingStalk) { return (farmerGerminatingStalk, stalk.sub(farmerGerminatingStalk)); } else { return (stalk, 0); } } }
/** * SPDX-License-Identifier: MIT **/ pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/SafeCast.sol"; import {LibAppStorage, Storage, AppStorage, Account} from "../LibAppStorage.sol"; import {C} from "../../C.sol"; import {LibSafeMath32} from "contracts/libraries/LibSafeMath32.sol"; import {LibSafeMath128} from "contracts/libraries/LibSafeMath128.sol"; import {LibSafeMathSigned128} from "contracts/libraries/LibSafeMathSigned128.sol"; import {LibSafeMathSigned96} from "contracts/libraries/LibSafeMathSigned96.sol"; import {LibBytes} from "contracts/libraries/LibBytes.sol"; import {LibGerminate} from "contracts/libraries/Silo/LibGerminate.sol"; import {LibWhitelistedTokens} from "contracts/libraries/Silo/LibWhitelistedTokens.sol"; /** * @title LibTokenSilo * @author Publius, Pizzaman1337 * @notice Contains functions for depositing, withdrawing and claiming * whitelisted Silo tokens. * * For functionality related to Stalk, and Roots, see {LibSilo}. */ library LibTokenSilo { using SafeMath for uint256; using LibSafeMath128 for uint128; using LibSafeMath32 for uint32; using LibSafeMathSigned128 for int128; using SafeCast for int128; using SafeCast for uint256; using LibSafeMathSigned96 for int96; uint256 constant PRECISION = 1e6; // increased precision from to silo v3.1. //////////////////////// ENUM //////////////////////// /** * @dev when a user deposits or withdraws a deposit, the * {TrasferSingle} event is emitted. However, in the case * of a transfer, this emission is ommited. This enum is * used to determine if the event should be emitted. */ enum Transfer { emitTransferSingle, noEmitTransferSingle } //////////////////////// EVENTS //////////////////////// /** * @dev IMPORTANT: copy of {TokenSilo-AddDeposit}, check there for details. */ event AddDeposit( address indexed account, address indexed token, int96 stem, uint256 amount, uint256 bdv ); /** * @dev IMPORTANT: copy of {TokenSilo-RemoveDeposit}, check there for details. */ event RemoveDeposit( address indexed account, address indexed token, int96 stem, uint256 amount, uint256 bdv ); // added as the ERC1155 deposit upgrade event TransferSingle( address indexed operator, address indexed sender, address indexed recipient, uint256 depositId, uint256 amount ); //////////////////////// ACCOUNTING: TOTALS GERMINATING //////////////////////// /** * @notice Increment the total amount and bdv of `token` germinating in the Silo. * @dev when an asset is `deposited` in the silo, it is not immediately eliable for * bean mints. It must `germinate` (stay deposited the silo) for a certain * amount of seasons (the remainer of the current season + 1). This function * increments the total amount and bdv germinating in the silo. The {sunrise} * function ends the germination process for even or odd germinating deposits. * * This protects beanstalk from flashloan attacks, and makes `totalDeposited` and * `totalDepositedBdv` significantly more MEV resistant. */ function incrementTotalGerminating( address token, uint256 amount, uint256 bdv, LibGerminate.Germinate germ ) internal { AppStorage storage s = LibAppStorage.diamondStorage(); Storage.TotalGerminating storage germinate; // verify germ is valid if (germ == LibGerminate.Germinate.ODD) { germinate = s.oddGerminating; } else if (germ == LibGerminate.Germinate.EVEN) { germinate = s.evenGerminating; } else { revert("invalid germinationMode"); // should not ever get here } // increment germinating amount and bdv. germinate.deposited[token].amount = germinate.deposited[token].amount.add( amount.toUint128() ); germinate.deposited[token].bdv = germinate.deposited[token].bdv.add(bdv.toUint128()); // emit event. emit LibGerminate.TotalGerminatingBalanceChanged( s.season.current, token, int256(amount), int256(bdv) ); } /** * @notice Decrement the total amount and bdv of `token` germinating in the Silo. * @dev `decrementTotalGerminating` should be used when removing deposits * that are < 2 seasons old. */ function decrementTotalGerminating( address token, uint256 amount, uint256 bdv, LibGerminate.Germinate germ ) internal { AppStorage storage s = LibAppStorage.diamondStorage(); Storage.TotalGerminating storage germinate; // verify germ is valid if (germ == LibGerminate.Germinate.ODD) { germinate = s.oddGerminating; } else if (germ == LibGerminate.Germinate.EVEN) { germinate = s.evenGerminating; } else { revert("invalid germinationMode"); // should not ever get here } // decrement germinating amount and bdv. germinate.deposited[token].amount = germinate.deposited[token].amount.sub( amount.toUint128() ); germinate.deposited[token].bdv = germinate.deposited[token].bdv.sub(bdv.toUint128()); emit LibGerminate.TotalGerminatingBalanceChanged( LibGerminate.getSeasonGerminationState() == germ ? s.season.current : s.season.current - 1, token, -int256(amount), -int256(bdv) ); } /** * @notice Increment the total bdv of `token` germinating in the Silo. Used in Enroot. */ function incrementTotalGerminatingBdv( address token, uint256 bdv, LibGerminate.Germinate germ ) internal { AppStorage storage s = LibAppStorage.diamondStorage(); if (germ == LibGerminate.Germinate.ODD) { // increment odd germinating s.oddGerminating.deposited[token].bdv = s.oddGerminating.deposited[token].bdv.add( bdv.toUint128() ); } else if (germ == LibGerminate.Germinate.EVEN) { // increment even germinating s.evenGerminating.deposited[token].bdv = s.evenGerminating.deposited[token].bdv.add( bdv.toUint128() ); } else { revert("invalid germinationMode"); // should not ever get here } emit LibGerminate.TotalGerminatingBalanceChanged( LibGerminate.getSeasonGerminationState() == germ ? s.season.current : s.season.current - 1, token, 0, int256(bdv) ); } //////////////////////// ACCOUNTING: TOTALS //////////////////////// /** * @dev Increment the total amount and bdv of `token` deposited in the Silo. * @dev `IncrementTotalDeposited` should be used when removing deposits that are * >= 2 seasons old (ex. when a user converts). */ function incrementTotalDeposited(address token, uint256 amount, uint256 bdv) internal { AppStorage storage s = LibAppStorage.diamondStorage(); s.siloBalances[token].deposited = s.siloBalances[token].deposited.add(amount.toUint128()); s.siloBalances[token].depositedBdv = s.siloBalances[token].depositedBdv.add( bdv.toUint128() ); } /** * @notice Decrement the total amount and bdv of `token` deposited in the Silo. * @dev `decrementTotalDeposited` should be used when removing deposits that are * >= 2 seasons old. */ function decrementTotalDeposited(address token, uint256 amount, uint256 bdv) internal { AppStorage storage s = LibAppStorage.diamondStorage(); s.siloBalances[token].deposited = s.siloBalances[token].deposited.sub(amount.toUint128()); s.siloBalances[token].depositedBdv = s.siloBalances[token].depositedBdv.sub( bdv.toUint128() ); } /** * @notice Increment the total bdv of `token` deposited in the Silo. Used in Enroot. */ function incrementTotalDepositedBdv(address token, uint256 bdv) internal { AppStorage storage s = LibAppStorage.diamondStorage(); s.siloBalances[token].depositedBdv = s.siloBalances[token].depositedBdv.add( bdv.toUint128() ); } //////////////////////// ADD DEPOSIT //////////////////////// /** * @return stalk The amount of Stalk received for this Deposit. * * @dev Calculate the current BDV for `amount` of `token`, then perform * Deposit accounting. */ function deposit( address account, address token, int96 stem, uint256 amount ) internal returns (uint256, LibGerminate.Germinate) { uint256 bdv = beanDenominatedValue(token, amount); return depositWithBDV(account, token, stem, amount, bdv); } /** * @dev Once the BDV received for Depositing `amount` of `token` is known, * add a Deposit for `account` and update the total amount Deposited. * * `s.ss[token].stalkIssuedPerBdv` stores the number of Stalk per BDV for `token`. */ function depositWithBDV( address account, address token, int96 stem, uint256 amount, uint256 bdv ) internal returns (uint256 stalk, LibGerminate.Germinate germ) { require(bdv > 0, "Silo: No Beans under Token."); AppStorage storage s = LibAppStorage.diamondStorage(); // determine whether the deposit is odd or even germinating germ = LibGerminate.getSeasonGerminationState(); // all new deposits will increment total germination. incrementTotalGerminating(token, amount, bdv, germ); addDepositToAccount( account, token, stem, amount, bdv, Transfer.emitTransferSingle ); stalk = bdv.mul(s.ss[token].stalkIssuedPerBdv); } /** * @dev Add `amount` of `token` to a user's Deposit in `stemTipForToken`. Requires a * precalculated `bdv`. * * If a Deposit doesn't yet exist, one is created. Otherwise, the existing * Deposit is updated. * * `amount` & `bdv` are downcasted uint256 -> uint128 to optimize storage cost, * since both values can be packed into one slot. * * Unlike {removeDepositFromAccount}, this function DOES EMIT an * {AddDeposit} event. See {removeDepositFromAccount} for more details. */ function addDepositToAccount( address account, address token, int96 stem, uint256 amount, uint256 bdv, Transfer transferType ) internal { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 depositId = LibBytes.packAddressAndStem(token, stem); // add amount and bdv to the deposits. s.a[account].deposits[depositId].amount = s.a[account].deposits[depositId].amount.add( amount.toUint128() ); s.a[account].deposits[depositId].bdv = s.a[account].deposits[depositId].bdv.add( bdv.toUint128() ); // SafeMath unnecessary b/c crateBDV <= type(uint128).max s.a[account].mowStatuses[token].bdv = s.a[account].mowStatuses[token].bdv.add( bdv.toUint128() ); /** * {addDepositToAccount} is used for both depositing and transferring deposits. * In the case of a deposit, only the {TransferSingle} Event needs to be emitted. * In the case of a transfer, a different {TransferSingle}/{TransferBatch} * Event is emitted in {TokenSilo._transferDeposit(s)}, * and thus, this event is ommited. */ if (transferType == Transfer.emitTransferSingle) { emit TransferSingle( msg.sender, // operator address(0), // from account, // to depositId, // depositID amount // token amount ); } emit AddDeposit(account, token, stem, amount, bdv); } //////////////////////// REMOVE DEPOSIT //////////////////////// /** * @dev Remove `amount` of `token` from a user's Deposit in `stem`. * * A "Crate" refers to the existing Deposit in storage at: * `s.a[account].deposits[token][stem]` * * Partially removing a Deposit should scale its BDV proportionally. For ex. * removing 80% of the tokens from a Deposit should reduce its BDV by 80%. * * During an update, `amount` & `bdv` are cast uint256 -> uint128 to * optimize storage cost, since both values can be packed into one slot. * * This function DOES **NOT** EMIT a {RemoveDeposit} event. This * asymmetry occurs because {removeDepositFromAccount} is called in a loop * in places where multiple deposits are removed simultaneously, including * {TokenSilo-removeDepositsFromAccount} and {TokenSilo-_transferDeposits}. */ function removeDepositFromAccount( address account, address token, int96 stem, uint256 amount ) internal returns (uint256 crateBDV) { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 depositId = LibBytes.packAddressAndStem(token, stem); uint256 crateAmount = s.a[account].deposits[depositId].amount; crateBDV = s.a[account].deposits[depositId].bdv; // if amount is > crateAmount, check if user has a legacy deposit: if (amount > crateAmount) { // get the absolute stem value. uint256 absStem = stem > 0 ? uint256(stem) : uint256(-stem); // only stems with modulo 1e6 can have a legacy deposit. if (absStem.mod(1e6) == 0) { (crateAmount, crateBDV) = migrateLegacyStemDeposit( account, token, stem, crateAmount, crateBDV ); } } require(amount <= crateAmount, "Silo: Crate balance too low."); // Partial remove if (amount < crateAmount) { // round up removal of BDV. (x - 1)/y + 1 // https://stackoverflow.com/questions/17944 uint256 removedBDV = amount.sub(1).mul(crateBDV).div(crateAmount).add(1); uint256 updatedBDV = crateBDV.sub(removedBDV); uint256 updatedAmount = crateAmount.sub(amount); // SafeCast unnecessary b/c updatedAmount <= crateAmount and updatedBDV <= crateBDV, // which are both <= type(uint128).max s.a[account].deposits[depositId].amount = updatedAmount.toUint128(); s.a[account].deposits[depositId].bdv = updatedBDV.toUint128(); s.a[account].mowStatuses[token].bdv = s.a[account].mowStatuses[token].bdv.sub( removedBDV.toUint128() ); return removedBDV; } // Full remove if (crateAmount > 0) delete s.a[account].deposits[depositId]; // SafeMath unnecessary b/c crateBDV <= type(uint128).max s.a[account].mowStatuses[token].bdv = s.a[account].mowStatuses[token].bdv.sub( crateBDV.toUint128() ); } //////////////////////// GETTERS //////////////////////// /** * @dev Calculate the BDV ("Bean Denominated Value") for `amount` of `token`. * * Makes a call to a BDV function defined in the SiloSettings for this * `token`. See {AppStorage.sol:Storage-SiloSettings} for more information. */ function beanDenominatedValue( address token, uint256 amount ) internal view returns (uint256 bdv) { AppStorage storage s = LibAppStorage.diamondStorage(); require(s.ss[token].selector != bytes4(0), "Silo: Token not whitelisted"); (bool success, bytes memory data) = address(this).staticcall( encodeBdvFunction(token, s.ss[token].encodeType, s.ss[token].selector, amount) ); if (!success) { if (data.length == 0) revert(); assembly { revert(add(32, data), mload(data)) } } assembly { bdv := mload(add(data, add(0x20, 0))) } } function encodeBdvFunction( address token, bytes1 encodeType, bytes4 selector, uint256 amount ) internal pure returns (bytes memory callData) { if (encodeType == 0x00) { callData = abi.encodeWithSelector(selector, amount); } else if (encodeType == 0x01) { callData = abi.encodeWithSelector(selector, token, amount); } else { revert("Silo: Invalid encodeType"); } } /** * @dev Locate the `amount` and `bdv` for a user's Deposit in storage. * * Silo V3 Deposits are stored within each {Account} as a mapping of: * `uint256 DepositID => { uint128 amount, uint128 bdv }` * The DepositID is the concatination of the token address and the stem. * * Silo V2 deposits are only usable after a successful migration, see * mowAndMigrate within the Migration facet. * */ function getDeposit( address account, address token, int96 stem ) internal view returns (uint256 amount, uint256 bdv) { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 depositId = LibBytes.packAddressAndStem(token, stem); amount = s.a[account].deposits[depositId].amount; bdv = s.a[account].deposits[depositId].bdv; } /** * @dev Get the number of Stalk per BDV per Season for a whitelisted token. * 6 decimal precision: 1e10 units = 1 stalk per season */ function stalkEarnedPerSeason(address token) internal view returns (uint256) { AppStorage storage s = LibAppStorage.diamondStorage(); return uint256(s.ss[token].stalkEarnedPerSeason); } /** * @dev Get the number of Stalk per BDV for a whitelisted token. Formerly just stalk. */ function stalkIssuedPerBdv(address token) internal view returns (uint256) { AppStorage storage s = LibAppStorage.diamondStorage(); return uint256(s.ss[token].stalkIssuedPerBdv); } /** * @dev returns the cumulative stalk per BDV (stemTip) for a whitelisted token. */ function stemTipForToken( address token ) internal view returns (int96 _stemTip) { AppStorage storage s = LibAppStorage.diamondStorage(); // SafeCast unnecessary because all casted variables are types smaller that int96. _stemTip = s.ss[token].milestoneStem + int96(s.ss[token].stalkEarnedPerSeason).mul( int96(s.season.current).sub(int96(s.ss[token].milestoneSeason)) ); } /** * @dev returns the amount of grown stalk a deposit has earned. */ function grownStalkForDeposit( address account, address token, int96 stem ) internal view returns (uint grownStalk) { // stemTipForToken(token) > depositGrownStalkPerBdv for all valid Deposits int96 _stemTip = stemTipForToken(token); require(stem <= _stemTip, "Silo: Invalid Deposit"); // The check in the above line guarantees that subtraction result is positive // and thus the cast to `uint256` is safe. uint deltaStemTip = uint256(_stemTip.sub(stem)); // no stalk has grown if the stem is equal to the stemTip. if (deltaStemTip == 0) return 0; (, uint bdv) = getDeposit(account, token, stem); grownStalk = deltaStemTip.mul(bdv).div(PRECISION); } /** * @dev returns the amount of grown stalk a deposit would have, based on the stem of the deposit. */ function calculateStalkFromStemAndBdv( address token, int96 grownStalkIndexOfDeposit, uint256 bdv ) internal view returns (int96 grownStalk) { // current latest grown stalk index int96 _stemTipForToken = stemTipForToken(address(token)); return _stemTipForToken.sub(grownStalkIndexOfDeposit).mul(toInt96(bdv)); } /** * @notice returns the grown stalk and germination state of a deposit, * based on the amount of grown stalk it has earned. */ function calculateStemForTokenFromGrownStalk( address token, uint256 grownStalk, uint256 bdv ) internal view returns (int96 stem, LibGerminate.Germinate germ) { LibGerminate.GermStem memory germStem = LibGerminate.getGerminatingStem(token); stem = germStem.stemTip.sub(toInt96(grownStalk.mul(PRECISION).div(bdv))); germ = LibGerminate._getGerminationState(stem, germStem); } /** * @dev returns the amount of grown stalk a deposit would have, based on the stem of the deposit. * Similar to calculateStalkFromStemAndBdv, but has an additional check to prevent division by 0. */ function grownStalkAndBdvToStem( address token, uint256 grownStalk, uint256 bdv ) internal view returns (int96 cumulativeGrownStalk) { // first get current latest grown stalk index int96 _stemTipForToken = stemTipForToken(token); // then calculate how much stalk each individual bdv has grown // there's a > 0 check here, because if you have a small amount of unripe bean deposit, the bdv could // end up rounding to zero, then you get a divide by zero error and can't migrate without losing that deposit // prevent divide by zero error int96 grownStalkPerBdv = bdv > 0 ? toInt96(grownStalk.mul(PRECISION).div(bdv)) : 0; // subtract from the current latest index, so we get the index the deposit should have happened at return _stemTipForToken.sub(grownStalkPerBdv); } /** * @notice internal logic for migrating a legacy deposit. * @dev */ function migrateLegacyStemDeposit( address account, address token, int96 newStem, uint256 crateAmount, uint256 crateBdv ) internal returns (uint256, uint256) { AppStorage storage s = LibAppStorage.diamondStorage(); // divide the newStem by 1e6 to get the legacy stem. uint256 legacyDepositId = LibBytes.packAddressAndStem(token, newStem.div(1e6)); uint256 legacyAmount = s.a[account].legacyV3Deposits[legacyDepositId].amount; uint256 legacyBdv = s.a[account].legacyV3Deposits[legacyDepositId].bdv; crateAmount = crateAmount.add(legacyAmount); crateBdv = crateBdv.add(legacyBdv); delete s.a[account].legacyV3Deposits[legacyDepositId]; // Emit burn events. emit TransferSingle( msg.sender, account, address(0), legacyDepositId, legacyAmount ); emit RemoveDeposit( account, token, newStem.div(1e6), legacyAmount, legacyBdv ); // Emit mint events. emit TransferSingle( msg.sender, address(0), account, LibBytes.packAddressAndStem(token, newStem), legacyAmount ); emit AddDeposit( account, token, newStem, legacyAmount, legacyBdv ); return (crateAmount, crateBdv); } function toInt96(uint256 value) internal pure returns (int96) { require(value <= uint256(type(int96).max), "SafeCast: value doesn't fit in an int96"); return int96(value); } }
/* SPDX-License-Identifier: MIT */ pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {C} from "../../C.sol"; import {LibAppStorage, AppStorage, Storage} from "../LibAppStorage.sol"; import {LibTokenSilo} from "contracts/libraries/Silo/LibTokenSilo.sol"; import {LibWhitelistedTokens} from "contracts/libraries/Silo/LibWhitelistedTokens.sol"; import {LibUnripe} from "contracts/libraries/LibUnripe.sol"; import {LibWell, IWell} from "contracts/libraries/Well/LibWell.sol"; import {LibSafeMath32} from "contracts/libraries/LibSafeMath32.sol"; /** * @title LibWhitelist * @author Publius * @notice Handles adding and removing ERC-20 tokens from the Silo Whitelist. */ library LibWhitelist { using LibSafeMath32 for uint32; /** * @notice Emitted when a token is added to the Silo Whitelist. * @param token ERC-20 token being added to the Silo Whitelist. * @param selector The function selector that returns the BDV of a given token. * @param stalkEarnedPerSeason The Stalk per BDV per Season received from depositing `token`. * @param stalkIssuedPerBdv The Stalk per BDV given from depositing `token`. * @param gpSelector The function selector that returns the gauge points of a given token. * @param lwSelector The function selector that returns the liquidity weight of a given token. * @param gaugePoints The gauge points of the token. * @param optimalPercentDepositedBdv The target percentage * of the total LP deposited BDV for this token. */ event WhitelistToken( address indexed token, bytes4 selector, uint32 stalkEarnedPerSeason, uint256 stalkIssuedPerBdv, bytes4 gpSelector, bytes4 lwSelector, uint128 gaugePoints, uint64 optimalPercentDepositedBdv ); /** * @notice Emitted when the gauge settings are updated. * @param token Token that is being updated. * @param gpSelector The new gaugePoint selector. * @param lwSelector The new liquidityWeight selector. * @param optimalPercentDepositedBdv The new optimal Percent deposited BDV */ event UpdateGaugeSettings( address indexed token, bytes4 gpSelector, bytes4 lwSelector, uint64 optimalPercentDepositedBdv ); /** * @notice Emitted when the stalk per bdv per season for a Silo token is updated. * @param token ERC-20 token being updated in the Silo Whitelist. * @param stalkEarnedPerSeason New stalk per bdv per season value for this token. * @param season The season that the new stalk per bdv per season value becomes active (The current season). */ event UpdatedStalkPerBdvPerSeason( address indexed token, uint32 stalkEarnedPerSeason, uint32 season ); /** * @notice Emitted when a token is removed from the Silo Whitelist. * @param token ERC-20 token being removed from the Silo Whitelist. */ event DewhitelistToken(address indexed token); /** * @dev Adds an ERC-20 token to the Silo Whitelist. * Assumes future tokens will be well pool tokens. */ function whitelistToken( address token, bytes4 selector, uint32 stalkIssuedPerBdv, uint32 stalkEarnedPerSeason, bytes1 encodeType, bytes4 gaugePointSelector, bytes4 liquidityWeightSelector, uint128 gaugePoints, uint64 optimalPercentDepositedBdv ) internal { AppStorage storage s = LibAppStorage.diamondStorage(); // verify the BDV, gaugePoint, and liquidityWeight selector. verifyBDVselector(token, encodeType, selector); verifyGaugePointSelector(gaugePointSelector); verifyLiquidityWeightSelector(liquidityWeightSelector); // verify whitelist status of token. // reverts on an invalid stalkIssuedPerBdv if previously whitelisted. verifyWhitelistStatus(token, selector, stalkIssuedPerBdv); // If an LP token, initialize oracle storage variables. if (token != address(C.bean()) && !LibUnripe.isUnripe(token)) { s.usdTokenPrice[token] = 1; s.twaReserves[token].reserve0 = 1; s.twaReserves[token].reserve1 = 1; } // beanstalk requires all whitelisted assets to have a minimum stalkEarnedPerSeason // of 1 (due to the germination update). set stalkEarnedPerSeason to 1 to prevent revert. if (stalkEarnedPerSeason == 0) stalkEarnedPerSeason = 1; s.ss[token].selector = selector; s.ss[token].stalkEarnedPerSeason = stalkEarnedPerSeason; s.ss[token].stalkIssuedPerBdv = stalkIssuedPerBdv; s.ss[token].milestoneSeason = uint32(s.season.current); s.ss[token].encodeType = encodeType; s.ss[token].gpSelector = gaugePointSelector; s.ss[token].lwSelector = liquidityWeightSelector; s.ss[token].gaugePoints = gaugePoints; s.ss[token].optimalPercentDepositedBdv = optimalPercentDepositedBdv; emit WhitelistToken( token, selector, stalkEarnedPerSeason, stalkIssuedPerBdv, gaugePointSelector, liquidityWeightSelector, gaugePoints, optimalPercentDepositedBdv ); } /** * @notice Updates optimalPercentDepositedBdv token. * @dev {LibWhitelistedTokens} must be updated to include the new token. */ function updateOptimalPercentDepositedBdvForToken( address token, uint64 optimalPercentDepositedBdv ) internal { Storage.SiloSettings storage ss = LibAppStorage.diamondStorage().ss[token]; updateGaugeForToken(token, ss.gpSelector, ss.lwSelector, optimalPercentDepositedBdv); } /** * @notice Updates gauge settings for token. * @dev {LibWhitelistedTokens} must be updated to include the new token. */ function updateGaugeForToken( address token, bytes4 gaugePointSelector, bytes4 liquidityWeightSelector, uint64 optimalPercentDepositedBdv ) internal { Storage.SiloSettings storage ss = LibAppStorage.diamondStorage().ss[token]; require(ss.selector != 0, "Whitelist: Token not whitelisted in Silo"); verifyGaugePointSelector(gaugePointSelector); verifyLiquidityWeightSelector(liquidityWeightSelector); ss.gpSelector = gaugePointSelector; ss.lwSelector = liquidityWeightSelector; ss.optimalPercentDepositedBdv = optimalPercentDepositedBdv; emit UpdateGaugeSettings( token, gaugePointSelector, liquidityWeightSelector, optimalPercentDepositedBdv ); } /** * @dev Updates the Stalk per BDV per Season for a token. */ function updateStalkPerBdvPerSeasonForToken( address token, uint32 stalkEarnedPerSeason ) internal { AppStorage storage s = LibAppStorage.diamondStorage(); require(s.ss[token].milestoneSeason != 0, "Token not whitelisted"); // beanstalk requires a min. stalkEarnedPerSeason of 1. if (stalkEarnedPerSeason == 0) stalkEarnedPerSeason = 1; // update milestone stem and season. s.ss[token].milestoneStem = LibTokenSilo.stemTipForToken(token); s.ss[token].milestoneSeason = s.season.current; // stalkEarnedPerSeason is set to int32 before casting down. // will overflow if s.ss[token].stalkEarnedPerSeason or // stalkEarnedPerSeason > 2^31 - 1. // or if the difference is greater than 2^23 - 1. s.ss[token].deltaStalkEarnedPerSeason = int24(int32(stalkEarnedPerSeason) - int32(s.ss[token].stalkEarnedPerSeason)); // calculate delta s.ss[token].stalkEarnedPerSeason = stalkEarnedPerSeason; emit UpdatedStalkPerBdvPerSeason(token, stalkEarnedPerSeason, s.season.current); } /** * @notice Removes an ERC-20 token from the Silo Whitelist. * */ function dewhitelistToken(address token) internal { AppStorage storage s = LibAppStorage.diamondStorage(); // before dewhitelisting, verify that `libWhitelistedTokens` are updated. LibWhitelistedTokens.updateWhitelistStatus(token, false, false, false); // set the stalkEarnedPerSeason to 1 and update milestone stem. // stalkEarnedPerSeason requires a min value of 1. updateStalkPerBdvPerSeasonForToken(token, 1); // delete the selector and encodeType. delete s.ss[token].selector; delete s.ss[token].encodeType; // delete gaugePoints, gaugePointSelector, liquidityWeightSelector, and optimalPercentDepositedBdv. delete s.ss[token].gaugePoints; delete s.ss[token].gpSelector; delete s.ss[token].lwSelector; delete s.ss[token].optimalPercentDepositedBdv; emit DewhitelistToken(token); } /** * @notice Verifies whether the bdv selector is valid. */ function verifyBDVselector(address token, bytes1 encodeType, bytes4 selector) internal view { (bool success, ) = address(this).staticcall( LibTokenSilo.encodeBdvFunction(token, encodeType, selector, 0) ); require(success, "Whitelist: Invalid BDV selector"); } /** * @notice Verifies whether the gaugePointSelector is valid for the gauge system. */ function verifyGaugePointSelector(bytes4 selector) internal view { // verify you passed in a callable gaugePoint selector (bool success, ) = address(this).staticcall(abi.encodeWithSelector(selector, 0, 0, 0)); require(success, "Whitelist: Invalid GaugePoint selector"); } /** * @notice Verifies whether the selector is valid for the gauge system. */ function verifyLiquidityWeightSelector(bytes4 selector) internal view { // verify you passed in a callable liquidityWeight selector (bool success, ) = address(this).staticcall(abi.encodeWithSelector(selector)); require(success, "Whitelist: Invalid LiquidityWeight selector"); } /** * @notice verifies whether a token is not whitelisted. * @dev if the token has been previously whitelisted, * return the current stalk issued per bdv. */ function verifyWhitelistStatus( address token, bytes4 selector, uint32 stalkIssuedPerBdv ) internal { AppStorage storage s = LibAppStorage.diamondStorage(); (bool isWhitelisted, bool previouslyWhitelisted) = LibWhitelistedTokens.checkWhitelisted(token); require(isWhitelisted == false, "Whitelist: Token already whitelisted"); // add whitelist status. If previously whitelisted, update the status rather than appending. if (previouslyWhitelisted) { LibWhitelistedTokens.updateWhitelistStatus( token, true, // Whitelisted by default. token != address(C.bean()) && !LibUnripe.isUnripe(token), // Assumes tokens that are not Unripe and not Bean are LP tokens. selector == LibWell.WELL_BDV_SELECTOR ); } else { // assumes new tokens are well pool tokens. LibWhitelistedTokens.addWhitelistStatus( token, true, // Whitelisted by default. token != address(C.bean()) && !LibUnripe.isUnripe(token), // Assumes tokens that are not Unripe and not Bean are LP tokens. selector == LibWell.WELL_BDV_SELECTOR ); } // if the token has previously been whitelisted, the stalkIssuedPerBdv // cannot be updated, as previous deposits would have been made with the // previous value. if (previouslyWhitelisted) { require(s.ss[token].stalkIssuedPerBdv == stalkIssuedPerBdv, "Whitelist: Cannot update stalkIssuedPerBdv"); } } }
/* SPDX-License-Identifier: MIT */ pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {C} from "../../C.sol"; import {AppStorage, Storage, LibAppStorage} from "contracts/libraries/LibAppStorage.sol"; /** * @title LibWhitelistedTokens * @author Brean, Brendan * @notice LibWhitelistedTokens holds different lists of types of Whitelisted Tokens. * * @dev manages the WhitelistStatuses for all tokens in the Silo in order to track lists. * Note: dewhitelisting a token doesn't remove it's WhitelistStatus entirely–It just modifies it. * Once a token has no more Deposits in the Silo, it's WhitelistStatus should be removed through calling `removeWhitelistStatus`. */ library LibWhitelistedTokens { /** * @notice Emitted when a Whitelis Status is added. */ event AddWhitelistStatus( address token, uint256 index, bool isWhitelisted, bool isWhitelistedLp, bool isWhitelistedWell ); /** * @notice Emitted when a Whitelist Status is removed. */ event RemoveWhitelistStatus( address token, uint256 index ); /** * @notice Emitted when a Whitelist Status is updated. */ event UpdateWhitelistStatus( address token, uint256 index, bool isWhitelisted, bool isWhitelistedLp, bool isWhitelistedWell ); /** * @notice Returns all tokens that are currently or previously in the silo, * including Unripe tokens. * @dev includes Dewhitelisted tokens with existing Deposits. */ function getSiloTokens() internal view returns (address[] memory tokens) { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 numberOfSiloTokens = s.whitelistStatuses.length; tokens = new address[](numberOfSiloTokens); for (uint256 i = 0; i < numberOfSiloTokens; i++) { tokens[i] = s.whitelistStatuses[i].token; } } /** * @notice Returns the current Whitelisted tokens, including Unripe tokens. */ function getWhitelistedTokens() internal view returns (address[] memory tokens) { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 numberOfSiloTokens = s.whitelistStatuses.length; uint256 tokensLength; tokens = new address[](numberOfSiloTokens); for (uint256 i = 0; i < numberOfSiloTokens; i++) { if (s.whitelistStatuses[i].isWhitelisted) { tokens[tokensLength++] = s.whitelistStatuses[i].token; } } assembly { mstore(tokens, tokensLength) } } /** * @notice Returns the current Whitelisted LP tokens. * @dev Unripe LP is not an LP token. */ function getWhitelistedLpTokens() internal view returns (address[] memory tokens) { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 numberOfSiloTokens = s.whitelistStatuses.length; uint256 tokensLength; tokens = new address[](numberOfSiloTokens); for (uint256 i = 0; i < numberOfSiloTokens; i++) { if (s.whitelistStatuses[i].isWhitelistedLp) { // assembly { // mstore(tokens, add(mload(tokens), 1)) // } tokens[tokensLength++] = s.whitelistStatuses[i].token; } } assembly { mstore(tokens, tokensLength) } } /** * @notice Returns the current Whitelisted Well LP tokens. */ function getWhitelistedWellLpTokens() internal view returns (address[] memory tokens) { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 numberOfSiloTokens = s.whitelistStatuses.length; uint256 tokensLength; tokens = new address[](numberOfSiloTokens); for (uint256 i = 0; i < numberOfSiloTokens; i++) { if (s.whitelistStatuses[i].isWhitelistedWell) { tokens[tokensLength++] = s.whitelistStatuses[i].token; } } assembly { mstore(tokens, tokensLength) } } /** * @notice Returns the Whitelist statues for all tokens that have been whitelisted and not manually removed. */ function getWhitelistedStatuses() internal view returns (Storage.WhitelistStatus[] memory _whitelistStatuses) { AppStorage storage s = LibAppStorage.diamondStorage(); _whitelistStatuses = s.whitelistStatuses; } /** * @notice Returns the Whitelist status for a given token. */ function getWhitelistedStatus(address token) internal view returns (Storage.WhitelistStatus memory _whitelistStatus) { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 tokenStatusIndex = findWhitelistStatusIndex(token); _whitelistStatus = s.whitelistStatuses[tokenStatusIndex]; } /** * @notice Adds a Whitelist Status for a given `token`. */ function addWhitelistStatus(address token, bool isWhitelisted, bool isWhitelistedLp, bool isWhitelistedWell) internal { AppStorage storage s = LibAppStorage.diamondStorage(); s.whitelistStatuses.push(Storage.WhitelistStatus( token, isWhitelisted, isWhitelistedLp, isWhitelistedWell )); emit AddWhitelistStatus(token, s.whitelistStatuses.length - 1, isWhitelisted, isWhitelistedLp, isWhitelistedWell); } /** * @notice Modifies the exisiting Whitelist Status of `token`. */ function updateWhitelistStatus(address token, bool isWhitelisted, bool isWhitelistedLp, bool isWhitelistedWell) internal { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 tokenStatusIndex = findWhitelistStatusIndex(token); s.whitelistStatuses[tokenStatusIndex].isWhitelisted = isWhitelisted; s.whitelistStatuses[tokenStatusIndex].isWhitelistedLp = isWhitelistedLp; s.whitelistStatuses[tokenStatusIndex].isWhitelistedWell = isWhitelistedWell; emit UpdateWhitelistStatus( token, tokenStatusIndex, isWhitelisted, isWhitelistedLp, isWhitelistedWell ); } /** * @notice Removes `token`'s Whitelist Status. */ function removeWhitelistStatus(address token) internal { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 tokenStatusIndex = findWhitelistStatusIndex(token); s.whitelistStatuses[tokenStatusIndex] = s.whitelistStatuses[s.whitelistStatuses.length - 1]; s.whitelistStatuses.pop(); emit RemoveWhitelistStatus(token, tokenStatusIndex); } /** * @notice Finds the index of a given `token`'s Whitelist Status. */ function findWhitelistStatusIndex(address token) private view returns (uint256) { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 whitelistedStatusLength = s.whitelistStatuses.length; uint256 i; while (s.whitelistStatuses[i].token != token) { i++; if (i >= whitelistedStatusLength) { revert("LibWhitelistedTokens: Token not found"); } } return i; } /** * @notice checks if a token is whitelisted. * @dev checks whether a token is in the whitelistStatuses array. If it is, * verify whether `isWhitelisted` is set to false. * @param token the token to check. */ function checkWhitelisted(address token) internal view returns (bool isWhitelisted, bool previouslyWhitelisted) { AppStorage storage s = LibAppStorage.diamondStorage(); uint256 whitelistedStatusLength = s.whitelistStatuses.length; uint256 i; while (s.whitelistStatuses[i].token != token) { i++; if (i >= whitelistedStatusLength) { // if the token does not appear in the array // it has not been whitelisted nor dewhitelisted. return (false, false); } } if (s.whitelistStatuses[i].isWhitelisted) { // token is whitelisted. return (true, false); } else { // token has been whitelisted previously. return (false, true); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.7.6; pragma experimental ABIEncoderV2; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import {Math} from "@openzeppelin/contracts/math/Math.sol"; import {SafeCast} from "@openzeppelin/contracts/utils/SafeCast.sol"; import {AppStorage, LibAppStorage} from "../LibAppStorage.sol"; /** * @title LibInternalBalance * @author LeoFib, Publius * @notice Handles internal read/write functions for Internal User Balances. * Largely inspired by Balancer's Vault. */ library LibBalance { using SafeERC20 for IERC20; using SafeMath for uint256; using SafeCast for uint256; /** * @notice Emitted when an account's Internal Balance changes. * @param account The account whose balance changed. * @param token Which token balance changed. * @param delta The amount the balance increased (if positive) or decreased (if negative). */ event InternalBalanceChanged( address indexed account, IERC20 indexed token, int256 delta ); /** * @dev Returns the sum of `account`'s Internal and External (ERC20) balance of `token` */ function getBalance(address account, IERC20 token) internal view returns (uint256 balance) { balance = token.balanceOf(account).add( getInternalBalance(account, token) ); return balance; } /** * @dev Increases `account`'s Internal Balance of `token` by `amount`. */ function increaseInternalBalance( address account, IERC20 token, uint256 amount ) internal { uint256 currentBalance = getInternalBalance(account, token); uint256 newBalance = currentBalance.add(amount); setInternalBalance(account, token, newBalance, amount.toInt256()); } /** * @dev Decreases `account`'s Internal Balance of `token` by `amount`. If `allowPartial` is true, this function * doesn't revert if `account` doesn't have enough balance, and sets it to zero and returns the deducted amount * instead. */ function decreaseInternalBalance( address account, IERC20 token, uint256 amount, bool allowPartial ) internal returns (uint256 deducted) { uint256 currentBalance = getInternalBalance(account, token); require( allowPartial || (currentBalance >= amount), "Balance: Insufficient internal balance" ); deducted = Math.min(currentBalance, amount); // By construction, `deducted` is lower or equal to `currentBalance`, // so we don't need to use checked arithmetic. uint256 newBalance = currentBalance - deducted; setInternalBalance(account, token, newBalance, -(deducted.toInt256())); } /** * @dev Sets `account`'s Internal Balance of `token` to `newBalance`. * * Emits an {InternalBalanceChanged} event. This event includes `delta`, which is the amount the balance increased * (if positive) or decreased (if negative). To avoid reading the current balance in order to compute the delta, * this function relies on the caller providing it directly. */ function setInternalBalance( address account, IERC20 token, uint256 newBalance, int256 delta ) private { AppStorage storage s = LibAppStorage.diamondStorage(); s.internalTokenBalance[account][token] = newBalance; emit InternalBalanceChanged(account, token, delta); } /** * @dev Returns `account`'s Internal Balance of `token`. */ function getInternalBalance(address account, IERC20 token) internal view returns (uint256 balance) { AppStorage storage s = LibAppStorage.diamondStorage(); balance = s.internalTokenBalance[account][token]; } }
// SPDX-License-Identifier: MIT pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "../../interfaces/IBean.sol"; import "./LibBalance.sol"; /** * @title LibTransfer * @author Publius * @notice Handles the recieving and sending of Tokens to/from internal Balances. */ library LibTransfer { using SafeERC20 for IERC20; using SafeMath for uint256; enum From { EXTERNAL, INTERNAL, EXTERNAL_INTERNAL, INTERNAL_TOLERANT } enum To { EXTERNAL, INTERNAL } function transferToken( IERC20 token, address sender, address recipient, uint256 amount, From fromMode, To toMode ) internal returns (uint256 transferredAmount) { if (fromMode == From.EXTERNAL && toMode == To.EXTERNAL) { uint256 beforeBalance = token.balanceOf(recipient); token.safeTransferFrom(sender, recipient, amount); return token.balanceOf(recipient).sub(beforeBalance); } amount = receiveToken(token, amount, sender, fromMode); sendToken(token, amount, recipient, toMode); return amount; } function receiveToken( IERC20 token, uint256 amount, address sender, From mode ) internal returns (uint256 receivedAmount) { if (amount == 0) return 0; if (mode != From.EXTERNAL) { receivedAmount = LibBalance.decreaseInternalBalance( sender, token, amount, mode != From.INTERNAL ); if (amount == receivedAmount || mode == From.INTERNAL_TOLERANT) return receivedAmount; } uint256 beforeBalance = token.balanceOf(address(this)); token.safeTransferFrom(sender, address(this), amount - receivedAmount); return receivedAmount.add( token.balanceOf(address(this)).sub(beforeBalance) ); } function sendToken( IERC20 token, uint256 amount, address recipient, To mode ) internal { if (amount == 0) return; if (mode == To.INTERNAL) LibBalance.increaseInternalBalance(recipient, token, amount); else token.safeTransfer(recipient, amount); } function burnToken( IBean token, uint256 amount, address sender, From mode ) internal returns (uint256 burnt) { // burnToken only can be called with Unripe Bean, Unripe Bean:3Crv or Bean token, which are all Beanstalk tokens. // Beanstalk's ERC-20 implementation uses OpenZeppelin's ERC20Burnable // which reverts if burnFrom function call cannot burn full amount. if (mode == From.EXTERNAL) { token.burnFrom(sender, amount); burnt = amount; } else { burnt = LibTransfer.receiveToken(token, amount, sender, mode); token.burn(burnt); } } function mintToken( IBean token, uint256 amount, address recipient, To mode ) internal { if (mode == To.EXTERNAL) { token.mint(recipient, amount); } else { token.mint(address(this), amount); LibTransfer.sendToken(token, amount, recipient, mode); } } }
/* SPDX-License-Identifier: MIT */ pragma solidity =0.7.6; pragma experimental ABIEncoderV2; import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol"; import {ICumulativePump} from "contracts/interfaces/basin/pumps/ICumulativePump.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IWell, Call} from "contracts/interfaces/basin/IWell.sol"; import {C} from "contracts/C.sol"; import {AppStorage, LibAppStorage, Storage} from "../LibAppStorage.sol"; import {LibUsdOracle} from "contracts/libraries/Oracle/LibUsdOracle.sol"; import {LibSafeMath128} from "contracts/libraries/LibSafeMath128.sol"; /** * @title Well Library * Contains helper functions for common Well related functionality. **/ library LibWell { using SafeMath for uint256; using LibSafeMath128 for uint128; // The BDV Selector that all Wells should be whitelisted with. bytes4 internal constant WELL_BDV_SELECTOR = 0xc84c7727; function getRatiosAndBeanIndex(IERC20[] memory tokens) internal view returns ( uint[] memory ratios, uint beanIndex, bool success ) { return getRatiosAndBeanIndex(tokens, 0); } /** * @dev Returns the price ratios between `tokens` and the index of Bean in `tokens`. * These actions are combined into a single function for gas efficiency. */ function getRatiosAndBeanIndex(IERC20[] memory tokens, uint256 lookback) internal view returns ( uint[] memory ratios, uint beanIndex, bool success ) { success = true; ratios = new uint[](tokens.length); beanIndex = type(uint256).max; for (uint i; i < tokens.length; ++i) { if (C.BEAN == address(tokens[i])) { beanIndex = i; ratios[i] = 1e6; } else { ratios[i] = LibUsdOracle.getUsdPrice(address(tokens[i]), lookback); if (ratios[i] == 0) { success = false; } } } require(beanIndex != type(uint256).max, "Bean not in Well."); } /** * @dev Returns the index of Bean in a list of tokens. */ function getBeanIndex(IERC20[] memory tokens) internal pure returns (uint beanIndex) { for (beanIndex; beanIndex < tokens.length; ++beanIndex) { if (C.BEAN == address(tokens[beanIndex])) { return beanIndex; } } revert("Bean not in Well."); } /** * @dev Returns the index of Bean given a Well. */ function getBeanIndexFromWell(address well) internal view returns (uint beanIndex) { IERC20[] memory tokens = IWell(well).tokens(); beanIndex = getBeanIndex(tokens); } /** * @dev Returns the non-Bean token within a Well. * Assumes a well with 2 tokens only, with Bean being one of them. * Cannot fail (and thus revert), as wells cannot have 2 of the same tokens as the pairing. */ function getNonBeanTokenAndIndexFromWell( address well ) internal view returns (address, uint256) { IERC20[] memory tokens = IWell(well).tokens(); for (uint256 i; i < tokens.length; i++) { if (address(tokens[i]) != C.BEAN) { return (address(tokens[i]), i); } } } /** * @dev Returns whether an address is a whitelisted Well by checking * if the BDV function selector is the `wellBdv` function. */ function isWell(address well) internal view returns (bool _isWell) { AppStorage storage s = LibAppStorage.diamondStorage(); return s.ss[well].selector == WELL_BDV_SELECTOR; } /** * @notice gets the non-bean usd liquidity of a well, * using the twa reserves and price in storage. * * @dev this is done for gas efficency purposes, rather than calling the pump multiple times. * This function should be called after the reserves for the well have been set. * Currently this is only done in {seasonFacet.sunrise}. * * if LibWell.getUsdTokenPriceForWell() returns 1, then this function is called without the reserves being set. * if s.usdTokenPrice[well] or s.twaReserves[well] returns 0, then the oracle failed to compute * a valid price this Season, and thus beanstalk cannot calculate the usd liquidity. */ function getWellTwaUsdLiquidityFromReserves( address well, uint256[] memory twaReserves ) internal view returns (uint256 usdLiquidity) { uint256 tokenUsd = getUsdTokenPriceForWell(well); (address token, uint256 j) = getNonBeanTokenAndIndexFromWell(well); if (tokenUsd > 1) { return twaReserves[j].mul(1e18).div(tokenUsd); } // if tokenUsd == 0, then the beanstalk could not compute a valid eth price, // and should return 0. if s.twaReserves[C.BEAN_ETH_WELL].reserve1 is 0, the previous if block will return 0. if (tokenUsd == 0) { return 0; } // if the function reaches here, then this is called outside the sunrise function // (i.e, seasonGetterFacet.getLiquidityToSupplyRatio()).We use LibUsdOracle // to get the price. This should never be reached during sunrise and thus // should not impact gas. return LibUsdOracle.getTokenPrice(token).mul(twaReserves[j]).div(1e6); } /** * @dev Sets the price in {AppStorage.usdTokenPrice} given a set of ratios. * It assumes that the ratios correspond to the Constant Product Well indexes. */ function setUsdTokenPriceForWell(address well, uint256[] memory ratios) internal { AppStorage storage s = LibAppStorage.diamondStorage(); // If the reserves length is 0, then {LibWellMinting} failed to compute // valid manipulation resistant reserves and thus the price is set to 0 // indicating that the oracle failed to compute a valid price this Season. if (ratios.length == 0) { s.usdTokenPrice[well] = 0; } else { (, uint256 j) = getNonBeanTokenAndIndexFromWell(well); s.usdTokenPrice[well] = ratios[j]; } } /** * @notice Returns the USD / TKN price stored in {AppStorage.usdTokenPrice}. * @dev assumes TKN has 18 decimals. */ function getUsdTokenPriceForWell(address well) internal view returns (uint tokenUsd) { tokenUsd = LibAppStorage.diamondStorage().usdTokenPrice[well]; } /** * @notice resets token price for a well to 1. * @dev must be called at the end of sunrise() once the * price is not needed anymore to save gas. */ function resetUsdTokenPriceForWell(address well) internal { LibAppStorage.diamondStorage().usdTokenPrice[well] = 1; } /** * @dev Sets the twaReserves in {AppStorage.usdTokenPrice}. * assumes the twaReserve indexes correspond to the Constant Product Well indexes. * if the length of the twaReserves is 0, then the minting oracle is off. * */ function setTwaReservesForWell(address well, uint256[] memory twaReserves) internal { AppStorage storage s = LibAppStorage.diamondStorage(); // if the length of twaReserves is 0, then return 0. // the length of twaReserves should never be 1, but // is added to prevent revert. if (twaReserves.length <= 1) { delete s.twaReserves[well].reserve0; delete s.twaReserves[well].reserve1; } else { // safeCast not needed as the reserves are uint128 in the wells. s.twaReserves[well].reserve0 = uint128(twaReserves[0]); s.twaReserves[well].reserve1 = uint128(twaReserves[1]); } } /** * @notice Returns the TKN / USD price stored in {AppStorage.usdTokenPrice}. * @dev assumes TKN has 18 decimals. */ function getTwaReservesForWell( address well ) internal view returns (uint256[] memory twaReserves) { AppStorage storage s = LibAppStorage.diamondStorage(); twaReserves = new uint256[](2); twaReserves[0] = s.twaReserves[well].reserve0; twaReserves[1] = s.twaReserves[well].reserve1; } /** * @notice resets token price for a well to 1. * @dev must be called at the end of sunrise() once the * price is not needed anymore to save gas. */ function resetTwaReservesForWell(address well) internal { AppStorage storage s = LibAppStorage.diamondStorage(); s.twaReserves[well].reserve0 = 1; s.twaReserves[well].reserve1 = 1; } /** * @notice returns the price in terms of TKN/BEAN. * (if eth is 1000 beans, this function will return 1000e6); */ function getBeanTokenPriceFromTwaReserves(address well) internal view returns (uint256 price) { AppStorage storage s = LibAppStorage.diamondStorage(); // s.twaReserve[well] should be set prior to this function being called. if (s.twaReserves[well].reserve0 == 0 || s.twaReserves[well].reserve1 == 0) { price = 0; } else { // fetch the bean index from the well in order to properly return the bean price. if (getBeanIndexFromWell(well) == 0) { price = uint256(s.twaReserves[well].reserve0).mul(1e18).div(s.twaReserves[well].reserve1); } else { price = uint256(s.twaReserves[well].reserve1).mul(1e18).div(s.twaReserves[well].reserve0); } } } function getTwaReservesFromStorageOrBeanstalkPump( address well ) internal view returns (uint256[] memory twaReserves) { twaReserves = getTwaReservesForWell(well); if (twaReserves[0] == 1) { twaReserves = getTwaReservesFromBeanstalkPump(well); } } /** * @notice gets the TwaReserves of a given well. * @dev only supports wells that are whitelisted in beanstalk. * the initial timestamp and reserves is the timestamp of the start * of the last season. wrapped in try/catch to return gracefully. */ function getTwaReservesFromBeanstalkPump( address well ) internal view returns (uint256[] memory) { AppStorage storage s = LibAppStorage.diamondStorage(); return getTwaReservesFromPump( well, s.wellOracleSnapshots[well], uint40(s.season.timestamp) ); } /** * @notice returns the twa reserves for well, * given the cumulative reserves and timestamp. * @dev wrapped in a try/catch to return gracefully. */ function getTwaReservesFromPump( address well, bytes memory cumulativeReserves, uint40 timestamp ) internal view returns (uint256[] memory) { Call[] memory pump = IWell(well).pumps(); try ICumulativePump(pump[0].target).readTwaReserves( well, cumulativeReserves, timestamp, pump[0].data ) returns (uint[] memory twaReserves, bytes memory) { return twaReserves; } catch { return (new uint256[](2)); } } /** * @notice gets the TwaLiquidity of a given well. * @dev only supports wells that are whitelisted in beanstalk. * the initial timestamp and reserves is the timestamp of the start * of the last season. */ function getTwaLiquidityFromBeanstalkPump( address well, uint256 tokenUsdPrice ) internal view returns (uint256 usdLiquidity) { AppStorage storage s = LibAppStorage.diamondStorage(); (, uint256 j) = getNonBeanTokenAndIndexFromWell(well); Call[] memory pumps = IWell(well).pumps(); try ICumulativePump(pumps[0].target).readTwaReserves( well, s.wellOracleSnapshots[well], uint40(s.season.timestamp), pumps[0].data ) returns (uint[] memory twaReserves, bytes memory) { usdLiquidity = tokenUsdPrice.mul(twaReserves[j]).div(1e6); } catch { // if pump fails to return a value, return 0. usdLiquidity = 0; } } }
{ "optimizer": { "enabled": true, "runs": 100 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": { "contracts/libraries/LibGauge.sol": { "LibGauge": "0x2d8ee96c167f8f42f0c447b830106179171aa9d5" }, "contracts/libraries/LibIncentive.sol": { "LibIncentive": "0xa1a7662d2afce76aac06460db7825cf99b7ec1b7" }, "contracts/libraries/LibLockedUnderlying.sol": { "LibLockedUnderlying": "0x165f9d2a986f70e472aa9569305105034a5dae2e" }, "contracts/libraries/Minting/LibWellMinting.sol": { "LibWellMinting": "0xb8e474d767d2278cd1d9cd05941182504befde96" }, "contracts/libraries/Silo/LibGerminate.sol": { "LibGerminate": "0x08a8e42bf3c4ef9608dc71a20106d1e028b97f71" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"season","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"caseId","type":"uint256"},{"indexed":false,"internalType":"int80","name":"absChange","type":"int80"}],"name":"BeanToMaxLpGpPerBdvRatioChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"season","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"toField","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toSilo","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toFertilizer","type":"uint256"}],"name":"Reward","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"season","type":"uint256"},{"indexed":false,"internalType":"address","name":"well","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toField","type":"uint256"}],"name":"SeasonOfPlenty","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"season","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"soil","type":"uint256"}],"name":"Soil","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"season","type":"uint256"}],"name":"Sunrise","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"season","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"caseId","type":"uint256"},{"indexed":false,"internalType":"int8","name":"absChange","type":"int8"}],"name":"TemperatureChange","type":"event"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"enum LibTransfer.To","name":"mode","type":"uint8"}],"name":"gm","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"seasonTime","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sunrise","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"payable","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b506154b6806100206000396000f3fe6080604052600436106100345760003560e01c806364ee4b8014610039578063ca7b7d7b14610062578063fc06d2a614610084575b600080fd5b61004c610047366004614aed565b61008c565b6040516100599190615099565b60405180910390f35b34801561006e57600080fd5b50610077610206565b6040516100599190615223565b61004c610243565b6000805a60025490915060ff16156100bf5760405162461bcd60e51b81526004016100b6906150ff565b60405180910390fd5b60035463ffffffff166100d0610206565b63ffffffff16116100f35760405162461bcd60e51b81526004016100b6906150a2565b60006100fd610255565b905060006101096102b8565b90507308a8e42bf3c4ef9608dc71a20106d1e028b97f7163d9d11f098361012e610388565b6040518363ffffffff1660e01b815260040161014b929190615234565b60006040518083038186803b15801561016357600080fd5b505af4158015610177573d6000803e3d6000fd5b50505050600061018682610481565b9050732d8ee96c167f8f42f0c447b830106179171aa9d56349f590446040518163ffffffff1660e01b815260040160006040518083038186803b1580156101cc57600080fd5b505af41580156101e0573d6000803e3d6000fd5b505050506101ee8282610557565b6101f98785886105ae565b9450505050505b92915050565b60045460009042101561021b57506000610240565b60055461022d575063ffffffff610240565b60055460045442038161023c57fe5b0490505b90565b600061025033600061008c565b905090565b6003805463ffffffff198116600163ffffffff9283160182161763ffffffff60981b198116600160981b438416021790925560405191169081907fb360bcf4b60112f485fd94b599df45181250ef0e80538be7b334728ab0990b1a90600090a290565b6000806102c361075e565b905060005b815181101561037f5761037573b8e474d767d2278cd1d9cd05941182504befde9663c49a3e928484815181106102fa57fe5b60200260200101516040518263ffffffff1660e01b815260040161031e9190614ef3565b60206040518083038186803b15801561033657600080fd5b505af415801561034a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061036e9190614db7565b849061084d565b92506001016102c8565b50504260065590565b606060006103946108b2565b60e48101549091506000816001600160401b03811180156103b457600080fd5b506040519080825280602002602001820160405280156103de578160200160208202803683370190505b50935060005b82811015610477578360e40181815481106103fb57fe5b600091825260209091200154600160a01b900460ff161561046f578360e401818154811061042557fe5b600091825260209091200154855160018401936001600160a01b03909216918791811061044e57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b6001016103e4565b5080845250505090565b60008061048c6108b7565b6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156104c457600080fd5b505afa1580156104d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104fc9190614db7565b9050806105225750506021805463ffffffff60c01b1916600160c01b1790556009610552565b600080600061053186856108cf565b9250925092506105418382610943565b61054b838361097d565b5090925050505b919050565b600082131561059057600061056b83610a7c565b90506105778183610b8d565b506003805460ff60b81b1916600160b81b1790556105aa565b61059c82600003610c2b565b6003805460ff60b81b191690555b5050565b60035460055460009182916105f191600c916105eb916105e4916105db9163ffffffff90811690610c9f16565b60045490610cf8565b4290610d40565b90610d8b565b9050600061061273bea0e11282e2bb5893bece110cf199501e872bad610df2565b9050600061061e61075e565b905060005b815181101561066c5761064882828151811061063b57fe5b6020026020010151610f0d565b61066482828151811061065757fe5b6020026020010151610f38565b600101610623565b5060405163720d945560e11b815260009073a1a7662d2afce76aac06460db7825cf99b7ec1b79063e41b28aa906106ab908a908890889060040161520d565b60206040518083038186803b1580156106c357600080fd5b505af41580156106d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106fb9190614db7565b90506107106107086108b7565b828a89610f81565b876001600160a01b03167fbb4f656853bc420ad6e4321622c07eefb4ed40e3f91b35553ce14a6dff4c0981826040516107499190615099565b60405180910390a293505050505b9392505050565b6060600061076a6108b2565b60e48101549091506000816001600160401b038111801561078a57600080fd5b506040519080825280602002602001820160405280156107b4578160200160208202803683370190505b50935060005b82811015610477578360e40181815481106107d157fe5b600091825260209091200154600160b01b900460ff1615610845578360e40181815481106107fb57fe5b600091825260209091200154855160018401936001600160a01b03909216918791811061082457fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b6001016107ba565b60008282018183128015906108625750838112155b80610877575060008312801561087757508381125b6107575760405162461bcd60e51b81526004018080602001828103825260218152602001806153636021913960400191505060405180910390fd5b600090565b73bea0000029ad1c77d3d5d23ba2d8893db9d1efab90565b6000806000806108de8561106c565b905060006109266108f28360200151611148565b61092061090285600001516111b4565b6109206109138c88606001516111f8565b6109208860400151611268565b90610cf8565b9050808260600151836080015194509450945050505b9250925092565b600061094e836112cc565b905061095e816020015184611311565b811561096a57506105aa565b6109788160600151846113f7565b505050565b600361098a836024611564565b10806109a05750600861099e836024611564565b115b156109ca57600354600160881b900460ff16156109c5576003805460ff60881b191690555b6105aa565b600354600160881b900460ff16610a405760038054600160881b60ff60881b1990911617808255600160681b80820463ffffffff9081166000908152603d6020526040808220549483168252902092909255825463ffffffff60681b198116921602179055600c54601955601d54601a556105aa565b601a54156105aa5760e5546001600160a01b0316610a745760e580546001600160a01b0319166001600160a01b0383161790555b6105aa6115c6565b600080610a876108b7565b6001600160a01b03166340c10f1930856040518363ffffffff1660e01b8152600401610ab4929190614f97565b600060405180830381600087803b158015610ace57600080fd5b505af1158015610ae2573d6000803e3d6000fd5b5050600354600160901b900460ff16159150610b12905057610b0383611873565b9050610b0f8382610d40565b92505b600c54600e541015610b3657610b27836119d2565b9150610b338383610d40565b92505b610b3f83611a12565b60035460405163ffffffff909116907f037e6634327a51e2bae1af6cf38c4d1cc3f7c97706d4ffa1936e5bfc84201a4890610b7f9085908790869061520d565b60405180910390a250919050565b602154600090610bb990606463ffffffff600160c01b909204821681018216916105eb918791610c9f16565b90506018610bc8836024611564565b10610bf257610beb670de0b6b3a76400006105eb836706f05b59d3b20000610c9f565b9050610c26565b6008610bff836024611564565b1015610c2657610c23670de0b6b3a76400006105eb836714d1120d7b160000610c9f565b90505b610978815b610c3481611b3b565b600b80546001600160801b0319166001600160801b039290921691909117905560035463ffffffff167f120fa8f6031fee45346a5c55083305b3840521c041af94971694219f7c26db8b610c8783611b3b565b604051610c9491906151d7565b60405180910390a250565b600082610cae57506000610200565b82820282848281610cbb57fe5b04146107575760405162461bcd60e51b81526004018080602001828103825260218152602001806153ca6021913960400191505060405180910390fd5b600082820183811015610757576040805162461bcd60e51b815260206004820152601b60248201526000805160206153aa833981519152604482015290519081900360640190fd5b600082821115610d85576040805162461bcd60e51b815260206004820152601e60248201526000805160206153eb833981519152604482015290519081900360640190fd5b50900390565b6000808211610de1576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b818381610dea57fe5b049392505050565b600080610dfd6108b2565b6001600160a01b0384166000908152604e820160205260409020549091506001600160801b03161580610e5857506001600160a01b0383166000908152604e82016020526040902054600160801b90046001600160801b0316155b15610e665760009150610f07565b610e6f83611b83565b610ebe576001600160a01b0383166000908152604e82016020526040902054610eb7906001600160801b03600160801b82048116916105eb9116670de0b6b3a7640000610c9f565b9150610f07565b6001600160a01b0383166000908152604e82016020526040902054610f04906001600160801b03808216916105eb91600160801b90910416670de0b6b3a7640000610c9f565b91505b50919050565b6001610f176108b2565b6001600160a01b039092166000908152604f92909201602052604090912055565b6000610f426108b2565b6001600160a01b039092166000908152604e909201602052506040902080546001600160801b0319166001176001600160801b0316600160801b179055565b6000816001811115610f8f57fe5b1415610ffa576040516340c10f1960e01b81526001600160a01b038516906340c10f1990610fc39085908790600401614f97565b600060405180830381600087803b158015610fdd57600080fd5b505af1158015610ff1573d6000803e3d6000fd5b50505050611066565b6040516340c10f1960e01b81526001600160a01b038516906340c10f19906110289030908790600401614f97565b600060405180830381600087803b15801561104257600080fd5b505af1158015611056573d6000803e3d6000fd5b5050505061106684848484611c06565b50505050565b61107461489e565b600061107e6108b2565b600b810180546001600160801b03808216909255919250600160801b909104166110a781611c48565b60218501805493875263ffffffff60a01b19909316600160a01b63ffffffff928316021763ffffffff60801b1916600160801b9290911691909102176001600160801b0319166001600160801b03831617905561110384611de3565b151560808601526001600160a01b031660608501526020840152600e820154600c83015461113b916111359190610d40565b85611fcf565b6040840152509092915050565b600061116561115e670b1a2bc2ec500000611ffe565b8390612018565b156111725750606c610552565b61118661115e67058d15e176280000611ffe565b1561119357506048610552565b6111a761115e6701aa535d3d0c0000611ffe565b1561055257506024919050565b60006111ca61115e670e92596fd6290000611ffe565b156111d757506002610552565b6111eb61115e670d2f13f7789f0000611ffe565b1561055257506001919050565b60008083131561020057600061120d83610df2565b9050600181111561125e5760006112446112308361122a8761202d565b90610c9f565b6c0c9f2c9cd04674edea4000000090610d8b565b90506210059081111561125c57600692505050610200565b505b5060039392505050565b600061127e61115e6703782dace9d90000611ffe565b1561128b5750601b610552565b61129f61115e670214e8348c4f0000611ffe565b156112ac57506012610552565b6112bf61115e66b1a2bc2ec50000611ffe565b1561055257506009919050565b6112d46148df565b60006112df83612059565b60d881901c60ff16600090810b900b602084015260381c6001600160501b0316600990810b900b606083015250919050565b602154600160c01b900463ffffffff16600083810b1215611387578260000360000b811161135b576021805463ffffffff60c01b1916600160c01b17905560018190039250611382565b6021805463ffffffff60c01b1916600160c01b6000868103900b840363ffffffff16021790555b6113ac565b6021805463ffffffff60c01b1916600160c01b600086900b840163ffffffff16021790555b60035460405163ffffffff909116907f90a0fbc380213286e93e058b5e853d1fc727fb5254d78e5ee43a842fbc1dd5a8906113ea90859087906151fc565b60405180910390a2505050565b605054600160801b90046001600160801b03166000600984900b121561148e578260000360090b6001600160801b0316816001600160801b03161161145157605080546001600160801b0316905560008190039250611489565b61146b6001600160801b038216600085900360090b61207e565b605080546001600160801b03928316600160801b0292169190911790555b611526565b68056bc75e2d631000006114af6001600160801b038316600986900b6120d5565b6001600160801b0316106114f1576114d068056bc75e2d631000008261207e565b605080546001600160801b03166556bc75e2d63160941b1790559250611526565b6115086001600160801b038216600985900b6120d5565b605080546001600160801b03928316600160801b0292169190911790555b60035460405163ffffffff909116907f8db44d78d51255c6e57bf14f04408783aa8b2524d7efe180df104f208199ae29906113ea90859087906151eb565b60008082116115b5576040805162461bcd60e51b8152602060048201526018602482015277536166654d6174683a206d6f64756c6f206279207a65726f60401b604482015290519081900360640190fd5b8183816115be57fe5b069392505050565b60e5546001600160a01b03166000806115de83612129565b9150915081600014156115f357505050611871565b601954600e54839160009110156116915750600e54601954819003906116199082610cf8565b600e556116246108b7565b6001600160a01b03166340c10f193061163d8486610cf8565b6040518363ffffffff1660e01b815260040161165a929190614f97565b600060405180830381600087803b15801561167457600080fd5b505af1158015611688573d6000803e3d6000fd5b505050506116f9565b6116996108b7565b6001600160a01b03166340c10f1930846040518363ffffffff1660e01b81526004016116c6929190614f97565b600060405180830381600087803b1580156116e057600080fd5b505af11580156116f4573d6000803e3d6000fd5b505050505b6117016108b7565b6001600160a01b031663095ea7b386846040518363ffffffff1660e01b815260040161172e929190614f97565b602060405180830381600087803b15801561174857600080fd5b505af115801561175c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117809190614d97565b506000856001600160a01b031663978b24ed61179a6108b7565b86866000306000196040518763ffffffff1660e01b81526004016117c39695949392919061505e565b602060405180830381600087803b1580156117dd57600080fd5b505af11580156117f1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118159190614db7565b9050611820816125de565b60035460405163ffffffff909116907f2d34ce594ac972d170fa0535659f8d28bd2bb778bf3923f6c8191753b8c0f56e90611862908990889086908890614f07565b60405180910390a25050505050505b565b600080611881836003610d8b565b604354909150600090611895908390610d8b565b6047549091506001600160801b031660006118b08284610cf8565b6046549091506001600160801b03165b808210611988576118d18184610d40565b6043549094506118ed906118e6908690610c9f565b8790610cf8565b95506118f761268a565b61195257604780546001600160801b0319166001600160801b0383161790556044546119239087610cf8565b6044819055604554146119485760405162461bcd60e51b81526004016100b6906150d9565b5050505050610552565b604354611963906105eb8789610d40565b93509150816119728185610cf8565b6046549092506001600160801b031690506118c0565b604780546001600160801b0319166001600160801b0384161790556043546119b5906118e6908690610c9f565b6044549096506119c59087610cf8565b6044555050505050919050565b600e54600c5460009190036119e8836002610d8b565b91508082116119f757816119f9565b805b600e54909250611a099083610cf8565b600e5550919050565b611a30611a1e82611b3b565b602254906001600160801b0316610cf8565b602255611a4b611a4282612710610c9f565b601b5490610cf8565b601b55611a96611a5a82611b3b565b73bea0000029ad1c77d3d5d23ba2d8893db9d1efab600052603860205260008051602061531c833981519152546001600160801b0316906120d5565b73bea0000029ad1c77d3d5d23ba2d8893db9d1efab600052603860205260008051602061531c83398151915280546001600160801b0319166001600160801b039283161790819055611af191600160801b90910416826120d5565b73bea0000029ad1c77d3d5d23ba2d8893db9d1efab600052603860205260008051602061531c83398151915280546001600160801b03928316600160801b02921691909117905550565b6000600160801b8210611b7f5760405162461bcd60e51b815260040180806020018281038252602781526020018061533c6027913960400191505060405180910390fd5b5090565b600080826001600160a01b0316639d63848a6040518163ffffffff1660e01b815260040160006040518083038186803b158015611bbf57600080fd5b505afa158015611bd3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611bfb9190810190614b28565b9050610f0481612757565b82611c1057611066565b6001816001811115611c1e57fe5b1415611c3457611c2f8285856127c9565b611066565b6110666001600160a01b0385168385612800565b611c50614906565b6000806000611c5d6108b2565b602181015490915063ffffffff600160a01b90910481161015611d80576021810154600160801b900463ffffffff9081161480611cad57506021810154610258600160a01b90910463ffffffff16105b80611d0c57506021810154603c600160801b90910463ffffffff16118015611d0c57506021810154611cf29063ffffffff600160801b909104811690603c9061285216565b602182015463ffffffff918216600160a01b909104909116105b15611d2957611d22670de0b6b3a76400006128a3565b9350611d7b565b6021810154611d4b9063ffffffff600160801b909104811690603c906128d116565b602182015463ffffffff918216600160a01b90910490911611611d7057611d22612922565b611d78612944565b93505b611dc7565b60218101546001600160801b031685611da257611d9b612944565b9450611dc5565b80611db857611d9b670de0b6b3a76400006128a3565b611dc28682611fcf565b94505b505b60210154929463ffffffff600160a01b90940484169450915050565b611deb614906565b6000806000611df86108b2565b905084611e1757611e07612944565b6000600193509350935050611fc8565b6000611e2161295f565b905060606000806000805b8551811015611f8457611e51868281518110611e4457fe5b6020026020010151612a4e565b94506000611e72878381518110611e6457fe5b602002602001015187612a81565b905080611e7e57600198505b611ee1670de0b6b3a76400006105eb8361122a8c60390160008d8981518110611ea357fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060010160049054906101000a900460e01b612b26565b925083831115611f0557829350868281518110611efa57fe5b602002602001015199505b611f0f8584610cf8565b9450611f19612bca565b6001600160a01b0316878381518110611f2e57fe5b60200260200101516001600160a01b03161415611f7b57611f4d6108b2565b60030154600160901b900460ff16151560011415611f7b57611f78611f7187612c5a565b8d90610d40565b9b505b50600101611e2c565b5082611fa757611f92612944565b60006001985098509850505050505050611fc8565b611fbf611fb98464e8d4a51000610d8b565b8b611fcf565b98505050505050505b9193909250565b611fd7614906565b6040518060200160405280611ff585670de0b6b3a764000086612d10565b90529392505050565b612006614906565b50604080516020810190915290815290565b6000806120258484612d28565b119392505050565b60006120376108b2565b6001600160a01b039092166000908152604f9290920160205250604090205490565b6000806120646108b2565b905080605101836090811061207557fe5b01549392505050565b6000826001600160801b0316826001600160801b03161115610d85576040805162461bcd60e51b815260206004820152601e60248201526000805160206153eb833981519152604482015290519081900360640190fd5b60008282016001600160801b038085169082161015610757576040805162461bcd60e51b815260206004820152601b60248201526000805160206153aa833981519152604482015290519081900360640190fd5b6000806001600160a01b038316612145575060009050806125d9565b60008390506000816001600160a01b0316639d63848a6040518163ffffffff1660e01b815260040160006040518083038186803b15801561218557600080fd5b505afa158015612199573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526121c19190810190614b28565b90506000826001600160a01b031663a1d89d966040518163ffffffff1660e01b815260040160006040518083038186803b1580156121fe57600080fd5b505afa158015612212573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261223a9190810190614c87565b905060008160008151811061224b57fe5b60200260200101516000015190506000816001600160a01b0316636de13cba898560008151811061227857fe5b6020026020010151602001516040518363ffffffff1660e01b81526004016122a1929190614f30565b60006040518083038186803b1580156122b957600080fd5b505afa1580156122cd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526122f59190810190614d0f565b90506000856001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160006040518083038186803b15801561233257600080fd5b505afa158015612346573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261236e9190810190614d0f565b90506000866001600160a01b03166310dd08306040518163ffffffff1660e01b815260040160006040518083038186803b1580156123ab57600080fd5b505afa1580156123bf573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526123e79190810190614dcf565b905060008060006123f789612d5a565b92509250925080612418576000809b509b50505050505050505050506125d9565b83516020850151604051631017d19360e31b81526000926001600160a01b0316916380be8c9891612451918b9188918a91600401614fd5565b60206040518083038186803b15801561246957600080fd5b505afa15801561247d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124a19190614db7565b9050600085600001516001600160a01b03166380be8c988886888a602001516040518563ffffffff1660e01b81526004016124df9493929190614fd5565b60206040518083038186803b1580156124f757600080fd5b505afa15801561250b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252f9190614db7565b9050600061255989868151811061254257fe5b602002602001015184612d7790919063ffffffff16565b9050600061256c89878151811061254257fe5b90508082131561257a578091505b600082121561259d576000809f509f5050505050505050505050505050506125d9565b819f508c866000146125b05760006125b3565b60015b60ff16815181106125c057fe5b60200260200101519e5050505050505050505050505050505b915091565b601a54612627906125fd906105eb8469d3c21bcecceda1000000610c9f565b60035463ffffffff64010000000090910481166000908152603d60205260409020549190610cf816565b6003805463ffffffff600160681b9182900481166000908152603d60205260409020939093558154600160481b64010000000092820485169290920267ffffffff0000000019909116179283160263ffffffff60481b1990921691909117905550565b6000806126956108b2565b60468101549091506001600160801b03166126bd6126b282612ddc565b604384015490610d40565b604383015560006126cd82612e0b565b90506001600160801b038116612721576043830154156126ff5760405162461bcd60e51b81526004016100b6906151a6565b50506000604682018190556003909101805460ff60901b191690559050610240565b61272a82612e0b565b60469390930180546001600160801b0319166001600160801b039094169390931790925550600192915050565b60005b81518110156127b15781818151811061276f57fe5b60200260200101516001600160a01b031673bea0000029ad1c77d3d5d23ba2d8893db9d1efab6001600160a01b031614156127a957610552565b60010161275a565b60405162461bcd60e51b81526004016100b69061517b565b60006127d58484612e3d565b905060006127e38284610cf8565b90506127f98585836127f487612e78565b612ebc565b5050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052610978908490612f37565b60008263ffffffff168263ffffffff161115610d85576040805162461bcd60e51b815260206004820152601e60248201526000805160206153eb833981519152604482015290519081900360640190fd5b6128ab614906565b6040805160208101909152806128c984670de0b6b3a7640000610c9f565b905292915050565b600082820163ffffffff8085169082161015610757576040805162461bcd60e51b815260206004820152601b60248201526000805160206153aa833981519152604482015290519081900360640190fd5b61292a614906565b506040805160208101909152670de0b6b3a7640000815290565b61294c614906565b5060408051602081019091526000815290565b6060600061296b6108b2565b60e48101549091506000816001600160401b038111801561298b57600080fd5b506040519080825280602002602001820160405280156129b5578160200160208202803683370190505b50935060005b82811015610477578360e40181815481106129d257fe5b600091825260209091200154600160a81b900460ff1615612a46578360e40181815481106129fc57fe5b600091825260209091200154855160018401936001600160a01b039092169187918110612a2557fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b6001016129bb565b6060612a5982612fe8565b905080600081518110612a6857fe5b60200260200101516001141561055257610200826130a4565b600080612a8d8461202d565b9050600080612a9b86613169565b915091506001831115612ae457612ada836105eb670de0b6b3a7640000888581518110612ac457fe5b6020026020010151610c9f90919063ffffffff16565b9350505050610200565b82612af55760009350505050610200565b612b1c620f42406105eb878481518110612b0b57fe5b602002602001015161122a86613260565b9695505050505050565b60408051600481526024810182526020810180516001600160e01b03166001600160e01b03198516179052905160009190829081903090612b68908590614ed7565b600060405180830381855afa9150503d8060008114612ba3576040519150601f19603f3d011682016040523d82523d6000602084013e612ba8565b606091505b509150915081612bbe5760009350505050610552565b60200151949350505050565b600080612bd56108b2565b731bea3ccd22f4ebd3d37d731ba31eeca95713716d600090815260408083016020529020549091506001600160a01b031615612c3e57731bea3ccd22f4ebd3d37d731ba31eeca95713716d600090815260408083016020529020546001600160a01b0316612c54565b73bea0000113b0d182f4064c86b71c315389e4715d5b91505090565b6000610200612c688361326d565b73165f9d2a986f70e472aa9569305105034a5dae2e63fc5a7bc0731bea0050e63e05fbb5d8ba2f10cf5800b6224449612ca3620f42406134c3565b6040518363ffffffff1660e01b8152600401612cc0929190614f97565b60206040518083038186803b158015612cd857600080fd5b505af4158015612cec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109209190614db7565b6000612d20826105eb8686610c9f565b949350505050565b805182516000911415612d3d57506001610200565b8151835111612d4d576000612d50565b60025b60ff169392505050565b6060600080612d6a8460006134ef565b9250925092509193909250565b6000818303818312801590612d8c5750838113155b80612da15750600083128015612da157508381135b6107575760405162461bcd60e51b81526004018080602001828103825260248152602001806154336024913960400191505060405180910390fd5b600080612de76108b2565b6001600160801b038416600090815260419091016020526040902054915050919050565b600080612e166108b2565b6001600160801b038085166000908152604290920160205260409091205416915050919050565b600080612e486108b2565b6001600160a01b039485166000908152603e90910160209081526040808320959096168252939093525050205490565b6000600160ff1b8210611b7f5760405162461bcd60e51b815260040180806020018281038252602881526020018061540b6028913960400191505060405180910390fd5b6000612ec66108b2565b6001600160a01b038087166000818152603e840160209081526040808320948a1680845294909152908190208790555192935090917f18e1ea4139e68413d7d08aa752e71568e36b2c5bf940893314c2c5b01eaa0c4290612f28908690615099565b60405180910390a35050505050565b6000612f8c826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166136339092919063ffffffff16565b80519091501561097857808060200190516020811015612fab57600080fd5b50516109785760405162461bcd60e51b815260040180806020018281038252602a815260200180615457602a913960400191505060405180910390fd5b60606000612ff46108b2565b60408051600280825260608201835292935091906020830190803683375050506001600160a01b0384166000908152604e8301602052604081205482519294506001600160801b03169184919061304757fe5b6020908102919091018101919091526001600160a01b0384166000908152604e830190915260409020548251600160801b9091046001600160801b0316908390600190811061309257fe5b60200260200101818152505050919050565b606060006130b06108b2565b6001600160a01b0384166000908152604b8201602090815260409182902080548351601f60026000196101006001861615020190931692909204918201849004840281018401909452808452939450610f04938793928301828280156131575780601f1061312c57610100808354040283529160200191613157565b820191906000526020600020905b81548152906001019060200180831161313a57829003601f168201915b50505050508360030160030154613642565b6000806000836001600160a01b0316639d63848a6040518163ffffffff1660e01b815260040160006040518083038186803b1580156131a757600080fd5b505afa1580156131bb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526131e39190810190614b28565b905060005b81518110156132595773bea0000029ad1c77d3d5d23ba2d8893db9d1efab6001600160a01b031682828151811061321b57fe5b60200260200101516001600160a01b0316146132515781818151811061323d57fe5b6020026020010151819350935050506125d9565b6001016131e8565b5050915091565b60006102008260006137a0565b6000806132786108b2565b90508260008151811061328757fe5b6020026020010151600014156132a1576000915050610552565b600073165f9d2a986f70e472aa9569305105034a5dae2e63fc5a7bc0731bea3ccd22f4ebd3d37d731ba31eeca95713716d6132de620f42406134c3565b6040518363ffffffff1660e01b81526004016132fb929190614f97565b60206040518083038186803b15801561331357600080fd5b505af4158015613327573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061334b9190614db7565b731bea3ccd22f4ebd3d37d731ba31eeca95713716d600090815260408481016020528120549192506001600160a01b039091169061338882611b83565b90506000826001600160a01b03166310dd08306040518163ffffffff1660e01b815260040160006040518083038186803b1580156133c557600080fd5b505afa1580156133d9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526134019190810190614dcf565b9050600081600001516001600160a01b03166314c15fc08984602001516040518363ffffffff1660e01b815260040161343b929190614fb0565b60206040518083038186803b15801561345357600080fd5b505afa158015613467573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061348b9190614db7565b90506134b7816105eb8a86815181106134a057fe5b602002602001015188610c9f90919063ffffffff16565b98975050505050505050565b6000806134ce6108b2565b9050610f0481604501546105eb858460440154610c9f90919063ffffffff16565b60606000806001905084516001600160401b038111801561350f57600080fd5b50604051908082528060200260200182016040528015613539578160200160208202803683370190505b509250600019915060005b85518110156136105785818151811061355957fe5b60200260200101516001600160a01b031673bea0000029ad1c77d3d5d23ba2d8893db9d1efab6001600160a01b031614156135b257809250620f42408482815181106135a157fe5b602002602001018181525050613608565b6135cf8682815181106135c157fe5b602002602001015186613832565b8482815181106135db57fe5b6020026020010181815250508381815181106135f357fe5b60200260200101516000141561360857600091505b600101613544565b5060001982141561093c5760405162461bcd60e51b81526004016100b69061517b565b6060612d2084846000856138c1565b60606000846001600160a01b031663a1d89d966040518163ffffffff1660e01b815260040160006040518083038186803b15801561367f57600080fd5b505afa158015613693573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526136bb9190810190614c87565b9050806000815181106136ca57fe5b6020026020010151600001516001600160a01b031663d393b27a868686856000815181106136f457fe5b6020026020010151602001516040518563ffffffff1660e01b815260040161371f9493929190614f54565b60006040518083038186803b15801561373757600080fd5b505afa92505050801561376c57506040513d6000823e601f3d908101601f191682016040526137699190810190614d41565b60015b61379657604080516002808252606082018352909160208301908036833701905050915050610757565b5091506107579050565b60006001600160a01b03831673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc214156137ea5760006137d283613a1c565b9050806137e3576000915050610200565b9050610200565b6001600160a01b038316737f39c581f595b53c5cb19bd0b3f8da6c935e2ca0141561381a5760006137d283613a6b565b60405162461bcd60e51b81526004016100b690615144565b60006001600160a01b03831673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2141561389157600061386483613a1c565b905080613875576000915050610200565b61388969d3c21bcecceda100000082610d8b565b915050610200565b6001600160a01b038316737f39c581f595b53c5cb19bd0b3f8da6c935e2ca0141561381a57600061386483613a6b565b6060824710156139025760405162461bcd60e51b81526004018080602001828103825260268152602001806153846026913960400191505060405180910390fd5b61390b85613a89565b61395c576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600080866001600160a01b031685876040518082805190602001908083835b6020831061399a5780518252601f19909201916020918201910161397b565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d80600081146139fc576040519150601f19603f3d011682016040523d82523d6000602084013e613a01565b606091505b5091509150613a11828286613a8f565b979650505050505050565b6000808211613a4a57613a45735f4ec3df9cbd43714fe2740f5e3616155c5b8419613840613b33565b610200565b610200735f4ec3df9cbd43714fe2740f5e3616155c5b841961384084613c91565b6000610200620f42406105eb613a8085613a1c565b61122a86613edc565b3b151590565b60608315613a9e575081610757565b825115613aae5782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015613af8578181015183820152602001613ae0565b50505050905090810190601f168015613b255780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b6000808390506000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015613b7457600080fd5b505afa925050508015613ba4575060408051601f3d908101601f19168201909252613ba191810190614e50565b60015b613bb357600092505050610200565b9050816001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a06040518083038186803b158015613bee57600080fd5b505afa925050508015613c1e575060408051601f3d908101601f19168201909252613c1b91810190614e01565b60015b613c2d57600092505050610200565b6001600160501b038516613c4b576000975050505050505050610200565b613c578285428c6140b5565b15613c6c576000975050505050505050610200565b613c8360ff8716600a0a6105eb86620f4240610c9f565b975050505050505050610200565b6000808490506000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015613cd257600080fd5b505afa925050508015613d02575060408051601f3d908101601f19168201909252613cff91810190614e50565b60015b613d1157600092505050610757565b9050816001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a06040518083038186803b158015613d4c57600080fd5b505afa925050508015613d7c575060408051601f3d908101601f19168201909252613d7991810190614e01565b60015b613d8b57600092505050610757565b6001600160501b038516613da9576000975050505050505050610757565b613db58285428d6140b5565b15613dca576000975050505050505050610757565b613dd2614919565b613ddc428b610d40565b602082018190528311613e0f57613e0060ff8816600a0a6105eb87620f4240610c9f565b98505050505050505050610757565b4260408201525b8060200151831115613e9b57613e4e613e46613e3f858460400151610d4090919063ffffffff16565b8790610c9f565b825190610cf8565b81526040810183905260001990950194613e6888876140f9565b8094508196505050613e80838683604001518e6140b5565b15613e9657600098505050505050505050610757565b613e16565b613ebc613e46613e3f83602001518460400151610d4090919063ffffffff16565b808252613e00908b906105eb9060ff8b16600a0a908290620f4240610c9f565b6000808215613f0c57613f077386392dc19c0b719886221c78ab11eb8cf5c528126205460085613c91565b613f2d565b613f2d7386392dc19c0b719886221c78ab11eb8cf5c5281262054600613b33565b905080613f3e576000915050610552565b6000737f39c581f595b53c5cb19bd0b3f8da6c935e2ca06001600160a01b031663035faf826040518163ffffffff1660e01b815260040160206040518083038186803b158015613f8d57600080fd5b505afa158015613fa1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613fc59190614db7565b9050613fd8620f42406105eb8484610c9f565b915063ffffffff841115613ff157600092505050610552565b600061405385156140025785614006565b6103845b73109830a1aaad605bbf02a9dfa7b0b92ec2fb7daa737f39c581f595b53c5cb19bd0b3f8da6c935e2ca073c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2670de0b6b3a7640000614197565b9050806140665760009350505050610552565b662386f26fc1000061407884836141d4565b10156140ad5761408d60026105eb8584610cf8565b93508184111561409b578193505b6140aa8464e8d4a51000610d8b565b93505b505050919050565b60008415806140c357508285115b156140d057506001612d20565b816140db8487610d40565b11156140e957506001612d20565b60008413612d2057506001612d20565b600080836001600160a01b0316639a6fc8f5846040518263ffffffff1660e01b81526004016141289190615283565b60a06040518083038186803b15801561414057600080fd5b505afa925050508015614170575060408051601f3d908101601f1916820190925261416d91810190614e01565b60015b614181575060001990506000614190565b50919450909250614190915050565b9250929050565b60008060006141a6878961423c565b91509150816141ba576000925050506141cb565b6141c6818588886143d9565b925050505b95945050505050565b6000818314156141e657506000610200565b8183101561421557614204826105eb85670de0b6b3a7640000610c9f565b670de0b6b3a7640000039050610200565b61422b836105eb84670de0b6b3a7640000610c9f565b670de0b6b3a7640000039392505050565b60008063ffffffff83166142625760405162461bcd60e51b81526004016100b690615128565b604080516002808252606082018352600092602083019080368337019050509050838160008151811061429157fe5b602002602001019063ffffffff16908163ffffffff16815250506000816001815181106142ba57fe5b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81526001600160a01b0386169063883bdbfd906142fb908490600401615014565b60006040518083038186803b15801561431357600080fd5b505afa92505050801561434857506040513d6000823e601f3d908101601f191682016040526143459190810190614bc0565b60015b614351576143d1565b60008260008151811061436057fe5b60200260200101518360018151811061437557fe5b60200260200101510390508663ffffffff168160060b8161439257fe5b05945060008160060b1280156143bc57508663ffffffff168160060b816143b557fe5b0760060b15155b156143c957600019909401935b600195505050505b509250929050565b6000806143e5866144c7565b90506001600160801b036001600160a01b03821611614454576001600160a01b03808216800290848116908616106144345761442f600160c01b876001600160801b0316836147ef565b61444c565b61444c81876001600160801b0316600160c01b6147ef565b9250506144be565b600061446e6001600160a01b03831680600160401b6147ef565b9050836001600160a01b0316856001600160a01b0316106144a6576144a1600160801b876001600160801b0316836147ef565b613a11565b613a1181876001600160801b0316600160801b6147ef565b50949350505050565b60008060008360020b126144de578260020b6144e6565b8260020b6000035b9050620d89e8811115614524576040805162461bcd60e51b81526020600482015260016024820152601560fa1b604482015290519081900360640190fd5b60006001821661453857600160801b61454a565b6ffffcb933bd6fad37aa2d162d1a5940015b6001600160881b031690506002821615614574576ffff97272373d413259a46990580e213a0260801c5b6004821615614593576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b60088216156145b2576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b60108216156145d1576fffcb9843d60f6159c9db58835c9266440260801c5b60208216156145f0576fff973b41fa98c081472e6896dfb254c00260801c5b604082161561460f576fff2ea16466c96a3843ec78b326b528610260801c5b608082161561462e576ffe5dee046a99a2a811c461f1969c30530260801c5b61010082161561464e576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b61020082161561466e576ff987a7253ac413176f2b074cf7815e540260801c5b61040082161561468e576ff3392b0822b70005940c7a398e4b70f30260801c5b6108008216156146ae576fe7159475a2c29b7443b29c7fa6e889d90260801c5b6110008216156146ce576fd097f3bdfd2022b8845ad8f792aa58250260801c5b6120008216156146ee576fa9f746462d870fdf8a65dc1f90e061e50260801c5b61400082161561470e576f70d869a156d2a1b890bb3df62baf32f70260801c5b61800082161561472e576f31be135f97d08fd981231505542fcfa60260801c5b6201000082161561474f576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b6202000082161561476f576e5d6af8dedb81196699c329225ee6040260801c5b6204000082161561478e576d2216e584f5fa1ea926041bedfe980260801c5b620800008216156147ab576b048a170391f7dc42444e8fa20260801c5b60008460020b13156147c65780600019816147c257fe5b0490505b6401000000008106156147da5760016147dd565b60005b60ff16602082901c0192505050919050565b6000808060001985870986860292508281109083900303905080614825576000841161481a57600080fd5b508290049050610757565b80841161483157600080fd5b6000848688096000868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b6040518060a001604052806148b1614906565b81526020016148be614906565b81526020016148cb614906565b815260006020820181905260409091015290565b60408051608081018252600080825260208201819052918101829052606081019190915290565b6040518060200160405280600081525090565b60405180606001604052806000815260200160008152602001600081525090565b600082601f83011261494a578081fd5b8151602061495f61495a836152ba565b615297565b828152818101908583018385028701840188101561497b578586fd5b855b858110156149a257815161499081615303565b8452928401929084019060010161497d565b5090979650505050505050565b600082601f8301126149bf578081fd5b815160206149cf61495a836152ba565b82815281810190858301838502870184018810156149eb578586fd5b855b858110156149a2578151845292840192908401906001016149ed565b600082601f830112614a19578081fd5b81516001600160401b03811115614a2c57fe5b614a3f601f8201601f1916602001615297565b818152846020838601011115614a53578283fd5b612d208260208301602087016152d7565b600060408284031215614a75578081fd5b604051604081016001600160401b038282108183111715614a9257fe5b8160405282935084519150614aa682615303565b90825260208401519080821115614abc57600080fd5b50614ac985828601614a09565b6020830152505092915050565b80516001600160501b038116811461055257600080fd5b60008060408385031215614aff578182fd5b8235614b0a81615303565b9150602083013560028110614b1d578182fd5b809150509250929050565b60006020808385031215614b3a578182fd5b82516001600160401b03811115614b4f578283fd5b8301601f81018513614b5f578283fd5b8051614b6d61495a826152ba565b8181528381019083850185840285018601891015614b89578687fd5b8694505b83851015614bb4578051614ba081615303565b835260019490940193918501918501614b8d565b50979650505050505050565b60008060408385031215614bd2578182fd5b82516001600160401b0380821115614be8578384fd5b818501915085601f830112614bfb578384fd5b81516020614c0b61495a836152ba565b82815281810190858301838502870184018b1015614c27578889fd5b8896505b84871015614c575780518060060b8114614c4357898afd5b835260019690960195918301918301614c2b565b5091880151919650909350505080821115614c70578283fd5b50614c7d8582860161493a565b9150509250929050565b60006020808385031215614c99578182fd5b82516001600160401b03811115614cae578283fd5b8301601f81018513614cbe578283fd5b8051614ccc61495a826152ba565b81815283810190838501865b84811015614d0157614cef8a888451890101614a64565b84529286019290860190600101614cd8565b509098975050505050505050565b600060208284031215614d20578081fd5b81516001600160401b03811115614d35578182fd5b612d20848285016149af565b60008060408385031215614d53578182fd5b82516001600160401b0380821115614d69578384fd5b614d75868387016149af565b93506020850151915080821115614d8a578283fd5b50614c7d85828601614a09565b600060208284031215614da8578081fd5b81518015158114610757578182fd5b600060208284031215614dc8578081fd5b5051919050565b600060208284031215614de0578081fd5b81516001600160401b03811115614df5578182fd5b612d2084828501614a64565b600080600080600060a08688031215614e18578081fd5b614e2186614ad6565b9450602086015193506040860151925060608601519150614e4460808701614ad6565b90509295509295909350565b600060208284031215614e61578081fd5b815160ff81168114610757578182fd5b6000815180845260208085019450808401835b83811015614ea057815187529582019590820190600101614e84565b509495945050505050565b60008151808452614ec38160208601602086016152d7565b601f01601f19169290920160200192915050565b60008251614ee98184602087016152d7565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b6001600160a01b0383168152604060208201819052600090612d2090830184614eab565b6001600160a01b0385168152608060208201819052600090614f7890830186614eab565b64ffffffffff851660408401528281036060840152613a118185614eab565b6001600160a01b03929092168252602082015260400190565b600060408252614fc36040830185614e71565b82810360208401526141cb8185614eab565b600060808252614fe86080830187614e71565b85602084015282810360408401526150008186614e71565b90508281036060840152613a118185614eab565b6020808252825182820181905260009190848201906040850190845b8181101561505257835163ffffffff1683529284019291840191600101615030565b50909695505050505050565b6001600160a01b039687168152948616602086015260408501939093526060840191909152909216608082015260a081019190915260c00190565b90815260200190565b6020808252601d908201527f536561736f6e3a205374696c6c2063757272656e7420536561736f6e2e000000604082015260600190565b6020808252600c908201526b14185a5908084f481bddd95960a21b604082015260600190565b6020808252600f908201526e29b2b0b9b7b71d102830bab9b2b21760891b604082015260600190565b602080825260029082015261042560f41b604082015260600190565b6020808252601c908201527f4f7261636c653a20546f6b656e206e6f7420737570706f727465642e00000000604082015260600190565b6020808252601190820152702132b0b7103737ba1034b7102bb2b6361760791b604082015260600190565b60208082526017908201527629ba34b6361030b1ba34bb32903332b93a34b634bd32b960491b604082015260600190565b6001600160801b0391909116815260200190565b91825260090b602082015260400190565b91825260000b602082015260400190565b9283526020830191909152604082015260600190565b63ffffffff91909116815260200190565b60006040820163ffffffff8516835260206040818501528185518084526060860191508287019350845b818110156149a25784516001600160a01b03168352938301939183019160010161525e565b6001600160501b0391909116815260200190565b6040518181016001600160401b03811182821017156152b257fe5b604052919050565b60006001600160401b038211156152cd57fe5b5060209081020190565b60005b838110156152f25781810151838201526020016152da565b838111156110665750506000910152565b6001600160a01b038116811461531857600080fd5b5056fe783e9dbd7ada0882f5e75946cdc58b9d3ff5bb4329ad861a80e70f123144955453616665436173743a2076616c756520646f65736e27742066697420696e2031323820626974735369676e6564536166654d6174683a206164646974696f6e206f766572666c6f77416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206164646974696f6e206f766572666c6f770000000000536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77536166654d6174683a207375627472616374696f6e206f766572666c6f77000053616665436173743a2076616c756520646f65736e27742066697420696e20616e20696e743235365369676e6564536166654d6174683a207375627472616374696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a2646970667358221220eedbd91f117a09013ab3562fff2141398d7c9ecc3031ce9cc6362111ba1282ca64736f6c63430007060033
Deployed Bytecode
0x6080604052600436106100345760003560e01c806364ee4b8014610039578063ca7b7d7b14610062578063fc06d2a614610084575b600080fd5b61004c610047366004614aed565b61008c565b6040516100599190615099565b60405180910390f35b34801561006e57600080fd5b50610077610206565b6040516100599190615223565b61004c610243565b6000805a60025490915060ff16156100bf5760405162461bcd60e51b81526004016100b6906150ff565b60405180910390fd5b60035463ffffffff166100d0610206565b63ffffffff16116100f35760405162461bcd60e51b81526004016100b6906150a2565b60006100fd610255565b905060006101096102b8565b90507308a8e42bf3c4ef9608dc71a20106d1e028b97f7163d9d11f098361012e610388565b6040518363ffffffff1660e01b815260040161014b929190615234565b60006040518083038186803b15801561016357600080fd5b505af4158015610177573d6000803e3d6000fd5b50505050600061018682610481565b9050732d8ee96c167f8f42f0c447b830106179171aa9d56349f590446040518163ffffffff1660e01b815260040160006040518083038186803b1580156101cc57600080fd5b505af41580156101e0573d6000803e3d6000fd5b505050506101ee8282610557565b6101f98785886105ae565b9450505050505b92915050565b60045460009042101561021b57506000610240565b60055461022d575063ffffffff610240565b60055460045442038161023c57fe5b0490505b90565b600061025033600061008c565b905090565b6003805463ffffffff198116600163ffffffff9283160182161763ffffffff60981b198116600160981b438416021790925560405191169081907fb360bcf4b60112f485fd94b599df45181250ef0e80538be7b334728ab0990b1a90600090a290565b6000806102c361075e565b905060005b815181101561037f5761037573b8e474d767d2278cd1d9cd05941182504befde9663c49a3e928484815181106102fa57fe5b60200260200101516040518263ffffffff1660e01b815260040161031e9190614ef3565b60206040518083038186803b15801561033657600080fd5b505af415801561034a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061036e9190614db7565b849061084d565b92506001016102c8565b50504260065590565b606060006103946108b2565b60e48101549091506000816001600160401b03811180156103b457600080fd5b506040519080825280602002602001820160405280156103de578160200160208202803683370190505b50935060005b82811015610477578360e40181815481106103fb57fe5b600091825260209091200154600160a01b900460ff161561046f578360e401818154811061042557fe5b600091825260209091200154855160018401936001600160a01b03909216918791811061044e57fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b6001016103e4565b5080845250505090565b60008061048c6108b7565b6001600160a01b03166318160ddd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156104c457600080fd5b505afa1580156104d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104fc9190614db7565b9050806105225750506021805463ffffffff60c01b1916600160c01b1790556009610552565b600080600061053186856108cf565b9250925092506105418382610943565b61054b838361097d565b5090925050505b919050565b600082131561059057600061056b83610a7c565b90506105778183610b8d565b506003805460ff60b81b1916600160b81b1790556105aa565b61059c82600003610c2b565b6003805460ff60b81b191690555b5050565b60035460055460009182916105f191600c916105eb916105e4916105db9163ffffffff90811690610c9f16565b60045490610cf8565b4290610d40565b90610d8b565b9050600061061273bea0e11282e2bb5893bece110cf199501e872bad610df2565b9050600061061e61075e565b905060005b815181101561066c5761064882828151811061063b57fe5b6020026020010151610f0d565b61066482828151811061065757fe5b6020026020010151610f38565b600101610623565b5060405163720d945560e11b815260009073a1a7662d2afce76aac06460db7825cf99b7ec1b79063e41b28aa906106ab908a908890889060040161520d565b60206040518083038186803b1580156106c357600080fd5b505af41580156106d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106fb9190614db7565b90506107106107086108b7565b828a89610f81565b876001600160a01b03167fbb4f656853bc420ad6e4321622c07eefb4ed40e3f91b35553ce14a6dff4c0981826040516107499190615099565b60405180910390a293505050505b9392505050565b6060600061076a6108b2565b60e48101549091506000816001600160401b038111801561078a57600080fd5b506040519080825280602002602001820160405280156107b4578160200160208202803683370190505b50935060005b82811015610477578360e40181815481106107d157fe5b600091825260209091200154600160b01b900460ff1615610845578360e40181815481106107fb57fe5b600091825260209091200154855160018401936001600160a01b03909216918791811061082457fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b6001016107ba565b60008282018183128015906108625750838112155b80610877575060008312801561087757508381125b6107575760405162461bcd60e51b81526004018080602001828103825260218152602001806153636021913960400191505060405180910390fd5b600090565b73bea0000029ad1c77d3d5d23ba2d8893db9d1efab90565b6000806000806108de8561106c565b905060006109266108f28360200151611148565b61092061090285600001516111b4565b6109206109138c88606001516111f8565b6109208860400151611268565b90610cf8565b9050808260600151836080015194509450945050505b9250925092565b600061094e836112cc565b905061095e816020015184611311565b811561096a57506105aa565b6109788160600151846113f7565b505050565b600361098a836024611564565b10806109a05750600861099e836024611564565b115b156109ca57600354600160881b900460ff16156109c5576003805460ff60881b191690555b6105aa565b600354600160881b900460ff16610a405760038054600160881b60ff60881b1990911617808255600160681b80820463ffffffff9081166000908152603d6020526040808220549483168252902092909255825463ffffffff60681b198116921602179055600c54601955601d54601a556105aa565b601a54156105aa5760e5546001600160a01b0316610a745760e580546001600160a01b0319166001600160a01b0383161790555b6105aa6115c6565b600080610a876108b7565b6001600160a01b03166340c10f1930856040518363ffffffff1660e01b8152600401610ab4929190614f97565b600060405180830381600087803b158015610ace57600080fd5b505af1158015610ae2573d6000803e3d6000fd5b5050600354600160901b900460ff16159150610b12905057610b0383611873565b9050610b0f8382610d40565b92505b600c54600e541015610b3657610b27836119d2565b9150610b338383610d40565b92505b610b3f83611a12565b60035460405163ffffffff909116907f037e6634327a51e2bae1af6cf38c4d1cc3f7c97706d4ffa1936e5bfc84201a4890610b7f9085908790869061520d565b60405180910390a250919050565b602154600090610bb990606463ffffffff600160c01b909204821681018216916105eb918791610c9f16565b90506018610bc8836024611564565b10610bf257610beb670de0b6b3a76400006105eb836706f05b59d3b20000610c9f565b9050610c26565b6008610bff836024611564565b1015610c2657610c23670de0b6b3a76400006105eb836714d1120d7b160000610c9f565b90505b610978815b610c3481611b3b565b600b80546001600160801b0319166001600160801b039290921691909117905560035463ffffffff167f120fa8f6031fee45346a5c55083305b3840521c041af94971694219f7c26db8b610c8783611b3b565b604051610c9491906151d7565b60405180910390a250565b600082610cae57506000610200565b82820282848281610cbb57fe5b04146107575760405162461bcd60e51b81526004018080602001828103825260218152602001806153ca6021913960400191505060405180910390fd5b600082820183811015610757576040805162461bcd60e51b815260206004820152601b60248201526000805160206153aa833981519152604482015290519081900360640190fd5b600082821115610d85576040805162461bcd60e51b815260206004820152601e60248201526000805160206153eb833981519152604482015290519081900360640190fd5b50900390565b6000808211610de1576040805162461bcd60e51b815260206004820152601a60248201527f536166654d6174683a206469766973696f6e206279207a65726f000000000000604482015290519081900360640190fd5b818381610dea57fe5b049392505050565b600080610dfd6108b2565b6001600160a01b0384166000908152604e820160205260409020549091506001600160801b03161580610e5857506001600160a01b0383166000908152604e82016020526040902054600160801b90046001600160801b0316155b15610e665760009150610f07565b610e6f83611b83565b610ebe576001600160a01b0383166000908152604e82016020526040902054610eb7906001600160801b03600160801b82048116916105eb9116670de0b6b3a7640000610c9f565b9150610f07565b6001600160a01b0383166000908152604e82016020526040902054610f04906001600160801b03808216916105eb91600160801b90910416670de0b6b3a7640000610c9f565b91505b50919050565b6001610f176108b2565b6001600160a01b039092166000908152604f92909201602052604090912055565b6000610f426108b2565b6001600160a01b039092166000908152604e909201602052506040902080546001600160801b0319166001176001600160801b0316600160801b179055565b6000816001811115610f8f57fe5b1415610ffa576040516340c10f1960e01b81526001600160a01b038516906340c10f1990610fc39085908790600401614f97565b600060405180830381600087803b158015610fdd57600080fd5b505af1158015610ff1573d6000803e3d6000fd5b50505050611066565b6040516340c10f1960e01b81526001600160a01b038516906340c10f19906110289030908790600401614f97565b600060405180830381600087803b15801561104257600080fd5b505af1158015611056573d6000803e3d6000fd5b5050505061106684848484611c06565b50505050565b61107461489e565b600061107e6108b2565b600b810180546001600160801b03808216909255919250600160801b909104166110a781611c48565b60218501805493875263ffffffff60a01b19909316600160a01b63ffffffff928316021763ffffffff60801b1916600160801b9290911691909102176001600160801b0319166001600160801b03831617905561110384611de3565b151560808601526001600160a01b031660608501526020840152600e820154600c83015461113b916111359190610d40565b85611fcf565b6040840152509092915050565b600061116561115e670b1a2bc2ec500000611ffe565b8390612018565b156111725750606c610552565b61118661115e67058d15e176280000611ffe565b1561119357506048610552565b6111a761115e6701aa535d3d0c0000611ffe565b1561055257506024919050565b60006111ca61115e670e92596fd6290000611ffe565b156111d757506002610552565b6111eb61115e670d2f13f7789f0000611ffe565b1561055257506001919050565b60008083131561020057600061120d83610df2565b9050600181111561125e5760006112446112308361122a8761202d565b90610c9f565b6c0c9f2c9cd04674edea4000000090610d8b565b90506210059081111561125c57600692505050610200565b505b5060039392505050565b600061127e61115e6703782dace9d90000611ffe565b1561128b5750601b610552565b61129f61115e670214e8348c4f0000611ffe565b156112ac57506012610552565b6112bf61115e66b1a2bc2ec50000611ffe565b1561055257506009919050565b6112d46148df565b60006112df83612059565b60d881901c60ff16600090810b900b602084015260381c6001600160501b0316600990810b900b606083015250919050565b602154600160c01b900463ffffffff16600083810b1215611387578260000360000b811161135b576021805463ffffffff60c01b1916600160c01b17905560018190039250611382565b6021805463ffffffff60c01b1916600160c01b6000868103900b840363ffffffff16021790555b6113ac565b6021805463ffffffff60c01b1916600160c01b600086900b840163ffffffff16021790555b60035460405163ffffffff909116907f90a0fbc380213286e93e058b5e853d1fc727fb5254d78e5ee43a842fbc1dd5a8906113ea90859087906151fc565b60405180910390a2505050565b605054600160801b90046001600160801b03166000600984900b121561148e578260000360090b6001600160801b0316816001600160801b03161161145157605080546001600160801b0316905560008190039250611489565b61146b6001600160801b038216600085900360090b61207e565b605080546001600160801b03928316600160801b0292169190911790555b611526565b68056bc75e2d631000006114af6001600160801b038316600986900b6120d5565b6001600160801b0316106114f1576114d068056bc75e2d631000008261207e565b605080546001600160801b03166556bc75e2d63160941b1790559250611526565b6115086001600160801b038216600985900b6120d5565b605080546001600160801b03928316600160801b0292169190911790555b60035460405163ffffffff909116907f8db44d78d51255c6e57bf14f04408783aa8b2524d7efe180df104f208199ae29906113ea90859087906151eb565b60008082116115b5576040805162461bcd60e51b8152602060048201526018602482015277536166654d6174683a206d6f64756c6f206279207a65726f60401b604482015290519081900360640190fd5b8183816115be57fe5b069392505050565b60e5546001600160a01b03166000806115de83612129565b9150915081600014156115f357505050611871565b601954600e54839160009110156116915750600e54601954819003906116199082610cf8565b600e556116246108b7565b6001600160a01b03166340c10f193061163d8486610cf8565b6040518363ffffffff1660e01b815260040161165a929190614f97565b600060405180830381600087803b15801561167457600080fd5b505af1158015611688573d6000803e3d6000fd5b505050506116f9565b6116996108b7565b6001600160a01b03166340c10f1930846040518363ffffffff1660e01b81526004016116c6929190614f97565b600060405180830381600087803b1580156116e057600080fd5b505af11580156116f4573d6000803e3d6000fd5b505050505b6117016108b7565b6001600160a01b031663095ea7b386846040518363ffffffff1660e01b815260040161172e929190614f97565b602060405180830381600087803b15801561174857600080fd5b505af115801561175c573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117809190614d97565b506000856001600160a01b031663978b24ed61179a6108b7565b86866000306000196040518763ffffffff1660e01b81526004016117c39695949392919061505e565b602060405180830381600087803b1580156117dd57600080fd5b505af11580156117f1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906118159190614db7565b9050611820816125de565b60035460405163ffffffff909116907f2d34ce594ac972d170fa0535659f8d28bd2bb778bf3923f6c8191753b8c0f56e90611862908990889086908890614f07565b60405180910390a25050505050505b565b600080611881836003610d8b565b604354909150600090611895908390610d8b565b6047549091506001600160801b031660006118b08284610cf8565b6046549091506001600160801b03165b808210611988576118d18184610d40565b6043549094506118ed906118e6908690610c9f565b8790610cf8565b95506118f761268a565b61195257604780546001600160801b0319166001600160801b0383161790556044546119239087610cf8565b6044819055604554146119485760405162461bcd60e51b81526004016100b6906150d9565b5050505050610552565b604354611963906105eb8789610d40565b93509150816119728185610cf8565b6046549092506001600160801b031690506118c0565b604780546001600160801b0319166001600160801b0384161790556043546119b5906118e6908690610c9f565b6044549096506119c59087610cf8565b6044555050505050919050565b600e54600c5460009190036119e8836002610d8b565b91508082116119f757816119f9565b805b600e54909250611a099083610cf8565b600e5550919050565b611a30611a1e82611b3b565b602254906001600160801b0316610cf8565b602255611a4b611a4282612710610c9f565b601b5490610cf8565b601b55611a96611a5a82611b3b565b73bea0000029ad1c77d3d5d23ba2d8893db9d1efab600052603860205260008051602061531c833981519152546001600160801b0316906120d5565b73bea0000029ad1c77d3d5d23ba2d8893db9d1efab600052603860205260008051602061531c83398151915280546001600160801b0319166001600160801b039283161790819055611af191600160801b90910416826120d5565b73bea0000029ad1c77d3d5d23ba2d8893db9d1efab600052603860205260008051602061531c83398151915280546001600160801b03928316600160801b02921691909117905550565b6000600160801b8210611b7f5760405162461bcd60e51b815260040180806020018281038252602781526020018061533c6027913960400191505060405180910390fd5b5090565b600080826001600160a01b0316639d63848a6040518163ffffffff1660e01b815260040160006040518083038186803b158015611bbf57600080fd5b505afa158015611bd3573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611bfb9190810190614b28565b9050610f0481612757565b82611c1057611066565b6001816001811115611c1e57fe5b1415611c3457611c2f8285856127c9565b611066565b6110666001600160a01b0385168385612800565b611c50614906565b6000806000611c5d6108b2565b602181015490915063ffffffff600160a01b90910481161015611d80576021810154600160801b900463ffffffff9081161480611cad57506021810154610258600160a01b90910463ffffffff16105b80611d0c57506021810154603c600160801b90910463ffffffff16118015611d0c57506021810154611cf29063ffffffff600160801b909104811690603c9061285216565b602182015463ffffffff918216600160a01b909104909116105b15611d2957611d22670de0b6b3a76400006128a3565b9350611d7b565b6021810154611d4b9063ffffffff600160801b909104811690603c906128d116565b602182015463ffffffff918216600160a01b90910490911611611d7057611d22612922565b611d78612944565b93505b611dc7565b60218101546001600160801b031685611da257611d9b612944565b9450611dc5565b80611db857611d9b670de0b6b3a76400006128a3565b611dc28682611fcf565b94505b505b60210154929463ffffffff600160a01b90940484169450915050565b611deb614906565b6000806000611df86108b2565b905084611e1757611e07612944565b6000600193509350935050611fc8565b6000611e2161295f565b905060606000806000805b8551811015611f8457611e51868281518110611e4457fe5b6020026020010151612a4e565b94506000611e72878381518110611e6457fe5b602002602001015187612a81565b905080611e7e57600198505b611ee1670de0b6b3a76400006105eb8361122a8c60390160008d8981518110611ea357fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060010160049054906101000a900460e01b612b26565b925083831115611f0557829350868281518110611efa57fe5b602002602001015199505b611f0f8584610cf8565b9450611f19612bca565b6001600160a01b0316878381518110611f2e57fe5b60200260200101516001600160a01b03161415611f7b57611f4d6108b2565b60030154600160901b900460ff16151560011415611f7b57611f78611f7187612c5a565b8d90610d40565b9b505b50600101611e2c565b5082611fa757611f92612944565b60006001985098509850505050505050611fc8565b611fbf611fb98464e8d4a51000610d8b565b8b611fcf565b98505050505050505b9193909250565b611fd7614906565b6040518060200160405280611ff585670de0b6b3a764000086612d10565b90529392505050565b612006614906565b50604080516020810190915290815290565b6000806120258484612d28565b119392505050565b60006120376108b2565b6001600160a01b039092166000908152604f9290920160205250604090205490565b6000806120646108b2565b905080605101836090811061207557fe5b01549392505050565b6000826001600160801b0316826001600160801b03161115610d85576040805162461bcd60e51b815260206004820152601e60248201526000805160206153eb833981519152604482015290519081900360640190fd5b60008282016001600160801b038085169082161015610757576040805162461bcd60e51b815260206004820152601b60248201526000805160206153aa833981519152604482015290519081900360640190fd5b6000806001600160a01b038316612145575060009050806125d9565b60008390506000816001600160a01b0316639d63848a6040518163ffffffff1660e01b815260040160006040518083038186803b15801561218557600080fd5b505afa158015612199573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526121c19190810190614b28565b90506000826001600160a01b031663a1d89d966040518163ffffffff1660e01b815260040160006040518083038186803b1580156121fe57600080fd5b505afa158015612212573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261223a9190810190614c87565b905060008160008151811061224b57fe5b60200260200101516000015190506000816001600160a01b0316636de13cba898560008151811061227857fe5b6020026020010151602001516040518363ffffffff1660e01b81526004016122a1929190614f30565b60006040518083038186803b1580156122b957600080fd5b505afa1580156122cd573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526122f59190810190614d0f565b90506000856001600160a01b0316630902f1ac6040518163ffffffff1660e01b815260040160006040518083038186803b15801561233257600080fd5b505afa158015612346573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261236e9190810190614d0f565b90506000866001600160a01b03166310dd08306040518163ffffffff1660e01b815260040160006040518083038186803b1580156123ab57600080fd5b505afa1580156123bf573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526123e79190810190614dcf565b905060008060006123f789612d5a565b92509250925080612418576000809b509b50505050505050505050506125d9565b83516020850151604051631017d19360e31b81526000926001600160a01b0316916380be8c9891612451918b9188918a91600401614fd5565b60206040518083038186803b15801561246957600080fd5b505afa15801561247d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124a19190614db7565b9050600085600001516001600160a01b03166380be8c988886888a602001516040518563ffffffff1660e01b81526004016124df9493929190614fd5565b60206040518083038186803b1580156124f757600080fd5b505afa15801561250b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061252f9190614db7565b9050600061255989868151811061254257fe5b602002602001015184612d7790919063ffffffff16565b9050600061256c89878151811061254257fe5b90508082131561257a578091505b600082121561259d576000809f509f5050505050505050505050505050506125d9565b819f508c866000146125b05760006125b3565b60015b60ff16815181106125c057fe5b60200260200101519e5050505050505050505050505050505b915091565b601a54612627906125fd906105eb8469d3c21bcecceda1000000610c9f565b60035463ffffffff64010000000090910481166000908152603d60205260409020549190610cf816565b6003805463ffffffff600160681b9182900481166000908152603d60205260409020939093558154600160481b64010000000092820485169290920267ffffffff0000000019909116179283160263ffffffff60481b1990921691909117905550565b6000806126956108b2565b60468101549091506001600160801b03166126bd6126b282612ddc565b604384015490610d40565b604383015560006126cd82612e0b565b90506001600160801b038116612721576043830154156126ff5760405162461bcd60e51b81526004016100b6906151a6565b50506000604682018190556003909101805460ff60901b191690559050610240565b61272a82612e0b565b60469390930180546001600160801b0319166001600160801b039094169390931790925550600192915050565b60005b81518110156127b15781818151811061276f57fe5b60200260200101516001600160a01b031673bea0000029ad1c77d3d5d23ba2d8893db9d1efab6001600160a01b031614156127a957610552565b60010161275a565b60405162461bcd60e51b81526004016100b69061517b565b60006127d58484612e3d565b905060006127e38284610cf8565b90506127f98585836127f487612e78565b612ebc565b5050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663a9059cbb60e01b179052610978908490612f37565b60008263ffffffff168263ffffffff161115610d85576040805162461bcd60e51b815260206004820152601e60248201526000805160206153eb833981519152604482015290519081900360640190fd5b6128ab614906565b6040805160208101909152806128c984670de0b6b3a7640000610c9f565b905292915050565b600082820163ffffffff8085169082161015610757576040805162461bcd60e51b815260206004820152601b60248201526000805160206153aa833981519152604482015290519081900360640190fd5b61292a614906565b506040805160208101909152670de0b6b3a7640000815290565b61294c614906565b5060408051602081019091526000815290565b6060600061296b6108b2565b60e48101549091506000816001600160401b038111801561298b57600080fd5b506040519080825280602002602001820160405280156129b5578160200160208202803683370190505b50935060005b82811015610477578360e40181815481106129d257fe5b600091825260209091200154600160a81b900460ff1615612a46578360e40181815481106129fc57fe5b600091825260209091200154855160018401936001600160a01b039092169187918110612a2557fe5b60200260200101906001600160a01b031690816001600160a01b0316815250505b6001016129bb565b6060612a5982612fe8565b905080600081518110612a6857fe5b60200260200101516001141561055257610200826130a4565b600080612a8d8461202d565b9050600080612a9b86613169565b915091506001831115612ae457612ada836105eb670de0b6b3a7640000888581518110612ac457fe5b6020026020010151610c9f90919063ffffffff16565b9350505050610200565b82612af55760009350505050610200565b612b1c620f42406105eb878481518110612b0b57fe5b602002602001015161122a86613260565b9695505050505050565b60408051600481526024810182526020810180516001600160e01b03166001600160e01b03198516179052905160009190829081903090612b68908590614ed7565b600060405180830381855afa9150503d8060008114612ba3576040519150601f19603f3d011682016040523d82523d6000602084013e612ba8565b606091505b509150915081612bbe5760009350505050610552565b60200151949350505050565b600080612bd56108b2565b731bea3ccd22f4ebd3d37d731ba31eeca95713716d600090815260408083016020529020549091506001600160a01b031615612c3e57731bea3ccd22f4ebd3d37d731ba31eeca95713716d600090815260408083016020529020546001600160a01b0316612c54565b73bea0000113b0d182f4064c86b71c315389e4715d5b91505090565b6000610200612c688361326d565b73165f9d2a986f70e472aa9569305105034a5dae2e63fc5a7bc0731bea0050e63e05fbb5d8ba2f10cf5800b6224449612ca3620f42406134c3565b6040518363ffffffff1660e01b8152600401612cc0929190614f97565b60206040518083038186803b158015612cd857600080fd5b505af4158015612cec573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109209190614db7565b6000612d20826105eb8686610c9f565b949350505050565b805182516000911415612d3d57506001610200565b8151835111612d4d576000612d50565b60025b60ff169392505050565b6060600080612d6a8460006134ef565b9250925092509193909250565b6000818303818312801590612d8c5750838113155b80612da15750600083128015612da157508381135b6107575760405162461bcd60e51b81526004018080602001828103825260248152602001806154336024913960400191505060405180910390fd5b600080612de76108b2565b6001600160801b038416600090815260419091016020526040902054915050919050565b600080612e166108b2565b6001600160801b038085166000908152604290920160205260409091205416915050919050565b600080612e486108b2565b6001600160a01b039485166000908152603e90910160209081526040808320959096168252939093525050205490565b6000600160ff1b8210611b7f5760405162461bcd60e51b815260040180806020018281038252602881526020018061540b6028913960400191505060405180910390fd5b6000612ec66108b2565b6001600160a01b038087166000818152603e840160209081526040808320948a1680845294909152908190208790555192935090917f18e1ea4139e68413d7d08aa752e71568e36b2c5bf940893314c2c5b01eaa0c4290612f28908690615099565b60405180910390a35050505050565b6000612f8c826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166136339092919063ffffffff16565b80519091501561097857808060200190516020811015612fab57600080fd5b50516109785760405162461bcd60e51b815260040180806020018281038252602a815260200180615457602a913960400191505060405180910390fd5b60606000612ff46108b2565b60408051600280825260608201835292935091906020830190803683375050506001600160a01b0384166000908152604e8301602052604081205482519294506001600160801b03169184919061304757fe5b6020908102919091018101919091526001600160a01b0384166000908152604e830190915260409020548251600160801b9091046001600160801b0316908390600190811061309257fe5b60200260200101818152505050919050565b606060006130b06108b2565b6001600160a01b0384166000908152604b8201602090815260409182902080548351601f60026000196101006001861615020190931692909204918201849004840281018401909452808452939450610f04938793928301828280156131575780601f1061312c57610100808354040283529160200191613157565b820191906000526020600020905b81548152906001019060200180831161313a57829003601f168201915b50505050508360030160030154613642565b6000806000836001600160a01b0316639d63848a6040518163ffffffff1660e01b815260040160006040518083038186803b1580156131a757600080fd5b505afa1580156131bb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526131e39190810190614b28565b905060005b81518110156132595773bea0000029ad1c77d3d5d23ba2d8893db9d1efab6001600160a01b031682828151811061321b57fe5b60200260200101516001600160a01b0316146132515781818151811061323d57fe5b6020026020010151819350935050506125d9565b6001016131e8565b5050915091565b60006102008260006137a0565b6000806132786108b2565b90508260008151811061328757fe5b6020026020010151600014156132a1576000915050610552565b600073165f9d2a986f70e472aa9569305105034a5dae2e63fc5a7bc0731bea3ccd22f4ebd3d37d731ba31eeca95713716d6132de620f42406134c3565b6040518363ffffffff1660e01b81526004016132fb929190614f97565b60206040518083038186803b15801561331357600080fd5b505af4158015613327573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061334b9190614db7565b731bea3ccd22f4ebd3d37d731ba31eeca95713716d600090815260408481016020528120549192506001600160a01b039091169061338882611b83565b90506000826001600160a01b03166310dd08306040518163ffffffff1660e01b815260040160006040518083038186803b1580156133c557600080fd5b505afa1580156133d9573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526134019190810190614dcf565b9050600081600001516001600160a01b03166314c15fc08984602001516040518363ffffffff1660e01b815260040161343b929190614fb0565b60206040518083038186803b15801561345357600080fd5b505afa158015613467573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061348b9190614db7565b90506134b7816105eb8a86815181106134a057fe5b602002602001015188610c9f90919063ffffffff16565b98975050505050505050565b6000806134ce6108b2565b9050610f0481604501546105eb858460440154610c9f90919063ffffffff16565b60606000806001905084516001600160401b038111801561350f57600080fd5b50604051908082528060200260200182016040528015613539578160200160208202803683370190505b509250600019915060005b85518110156136105785818151811061355957fe5b60200260200101516001600160a01b031673bea0000029ad1c77d3d5d23ba2d8893db9d1efab6001600160a01b031614156135b257809250620f42408482815181106135a157fe5b602002602001018181525050613608565b6135cf8682815181106135c157fe5b602002602001015186613832565b8482815181106135db57fe5b6020026020010181815250508381815181106135f357fe5b60200260200101516000141561360857600091505b600101613544565b5060001982141561093c5760405162461bcd60e51b81526004016100b69061517b565b6060612d2084846000856138c1565b60606000846001600160a01b031663a1d89d966040518163ffffffff1660e01b815260040160006040518083038186803b15801561367f57600080fd5b505afa158015613693573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526136bb9190810190614c87565b9050806000815181106136ca57fe5b6020026020010151600001516001600160a01b031663d393b27a868686856000815181106136f457fe5b6020026020010151602001516040518563ffffffff1660e01b815260040161371f9493929190614f54565b60006040518083038186803b15801561373757600080fd5b505afa92505050801561376c57506040513d6000823e601f3d908101601f191682016040526137699190810190614d41565b60015b61379657604080516002808252606082018352909160208301908036833701905050915050610757565b5091506107579050565b60006001600160a01b03831673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc214156137ea5760006137d283613a1c565b9050806137e3576000915050610200565b9050610200565b6001600160a01b038316737f39c581f595b53c5cb19bd0b3f8da6c935e2ca0141561381a5760006137d283613a6b565b60405162461bcd60e51b81526004016100b690615144565b60006001600160a01b03831673c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2141561389157600061386483613a1c565b905080613875576000915050610200565b61388969d3c21bcecceda100000082610d8b565b915050610200565b6001600160a01b038316737f39c581f595b53c5cb19bd0b3f8da6c935e2ca0141561381a57600061386483613a6b565b6060824710156139025760405162461bcd60e51b81526004018080602001828103825260268152602001806153846026913960400191505060405180910390fd5b61390b85613a89565b61395c576040805162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015290519081900360640190fd5b600080866001600160a01b031685876040518082805190602001908083835b6020831061399a5780518252601f19909201916020918201910161397b565b6001836020036101000a03801982511681845116808217855250505050505090500191505060006040518083038185875af1925050503d80600081146139fc576040519150601f19603f3d011682016040523d82523d6000602084013e613a01565b606091505b5091509150613a11828286613a8f565b979650505050505050565b6000808211613a4a57613a45735f4ec3df9cbd43714fe2740f5e3616155c5b8419613840613b33565b610200565b610200735f4ec3df9cbd43714fe2740f5e3616155c5b841961384084613c91565b6000610200620f42406105eb613a8085613a1c565b61122a86613edc565b3b151590565b60608315613a9e575081610757565b825115613aae5782518084602001fd5b8160405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b83811015613af8578181015183820152602001613ae0565b50505050905090810190601f168015613b255780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b6000808390506000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015613b7457600080fd5b505afa925050508015613ba4575060408051601f3d908101601f19168201909252613ba191810190614e50565b60015b613bb357600092505050610200565b9050816001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a06040518083038186803b158015613bee57600080fd5b505afa925050508015613c1e575060408051601f3d908101601f19168201909252613c1b91810190614e01565b60015b613c2d57600092505050610200565b6001600160501b038516613c4b576000975050505050505050610200565b613c578285428c6140b5565b15613c6c576000975050505050505050610200565b613c8360ff8716600a0a6105eb86620f4240610c9f565b975050505050505050610200565b6000808490506000816001600160a01b031663313ce5676040518163ffffffff1660e01b815260040160206040518083038186803b158015613cd257600080fd5b505afa925050508015613d02575060408051601f3d908101601f19168201909252613cff91810190614e50565b60015b613d1157600092505050610757565b9050816001600160a01b031663feaf968c6040518163ffffffff1660e01b815260040160a06040518083038186803b158015613d4c57600080fd5b505afa925050508015613d7c575060408051601f3d908101601f19168201909252613d7991810190614e01565b60015b613d8b57600092505050610757565b6001600160501b038516613da9576000975050505050505050610757565b613db58285428d6140b5565b15613dca576000975050505050505050610757565b613dd2614919565b613ddc428b610d40565b602082018190528311613e0f57613e0060ff8816600a0a6105eb87620f4240610c9f565b98505050505050505050610757565b4260408201525b8060200151831115613e9b57613e4e613e46613e3f858460400151610d4090919063ffffffff16565b8790610c9f565b825190610cf8565b81526040810183905260001990950194613e6888876140f9565b8094508196505050613e80838683604001518e6140b5565b15613e9657600098505050505050505050610757565b613e16565b613ebc613e46613e3f83602001518460400151610d4090919063ffffffff16565b808252613e00908b906105eb9060ff8b16600a0a908290620f4240610c9f565b6000808215613f0c57613f077386392dc19c0b719886221c78ab11eb8cf5c528126205460085613c91565b613f2d565b613f2d7386392dc19c0b719886221c78ab11eb8cf5c5281262054600613b33565b905080613f3e576000915050610552565b6000737f39c581f595b53c5cb19bd0b3f8da6c935e2ca06001600160a01b031663035faf826040518163ffffffff1660e01b815260040160206040518083038186803b158015613f8d57600080fd5b505afa158015613fa1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613fc59190614db7565b9050613fd8620f42406105eb8484610c9f565b915063ffffffff841115613ff157600092505050610552565b600061405385156140025785614006565b6103845b73109830a1aaad605bbf02a9dfa7b0b92ec2fb7daa737f39c581f595b53c5cb19bd0b3f8da6c935e2ca073c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2670de0b6b3a7640000614197565b9050806140665760009350505050610552565b662386f26fc1000061407884836141d4565b10156140ad5761408d60026105eb8584610cf8565b93508184111561409b578193505b6140aa8464e8d4a51000610d8b565b93505b505050919050565b60008415806140c357508285115b156140d057506001612d20565b816140db8487610d40565b11156140e957506001612d20565b60008413612d2057506001612d20565b600080836001600160a01b0316639a6fc8f5846040518263ffffffff1660e01b81526004016141289190615283565b60a06040518083038186803b15801561414057600080fd5b505afa925050508015614170575060408051601f3d908101601f1916820190925261416d91810190614e01565b60015b614181575060001990506000614190565b50919450909250614190915050565b9250929050565b60008060006141a6878961423c565b91509150816141ba576000925050506141cb565b6141c6818588886143d9565b925050505b95945050505050565b6000818314156141e657506000610200565b8183101561421557614204826105eb85670de0b6b3a7640000610c9f565b670de0b6b3a7640000039050610200565b61422b836105eb84670de0b6b3a7640000610c9f565b670de0b6b3a7640000039392505050565b60008063ffffffff83166142625760405162461bcd60e51b81526004016100b690615128565b604080516002808252606082018352600092602083019080368337019050509050838160008151811061429157fe5b602002602001019063ffffffff16908163ffffffff16815250506000816001815181106142ba57fe5b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81526001600160a01b0386169063883bdbfd906142fb908490600401615014565b60006040518083038186803b15801561431357600080fd5b505afa92505050801561434857506040513d6000823e601f3d908101601f191682016040526143459190810190614bc0565b60015b614351576143d1565b60008260008151811061436057fe5b60200260200101518360018151811061437557fe5b60200260200101510390508663ffffffff168160060b8161439257fe5b05945060008160060b1280156143bc57508663ffffffff168160060b816143b557fe5b0760060b15155b156143c957600019909401935b600195505050505b509250929050565b6000806143e5866144c7565b90506001600160801b036001600160a01b03821611614454576001600160a01b03808216800290848116908616106144345761442f600160c01b876001600160801b0316836147ef565b61444c565b61444c81876001600160801b0316600160c01b6147ef565b9250506144be565b600061446e6001600160a01b03831680600160401b6147ef565b9050836001600160a01b0316856001600160a01b0316106144a6576144a1600160801b876001600160801b0316836147ef565b613a11565b613a1181876001600160801b0316600160801b6147ef565b50949350505050565b60008060008360020b126144de578260020b6144e6565b8260020b6000035b9050620d89e8811115614524576040805162461bcd60e51b81526020600482015260016024820152601560fa1b604482015290519081900360640190fd5b60006001821661453857600160801b61454a565b6ffffcb933bd6fad37aa2d162d1a5940015b6001600160881b031690506002821615614574576ffff97272373d413259a46990580e213a0260801c5b6004821615614593576ffff2e50f5f656932ef12357cf3c7fdcc0260801c5b60088216156145b2576fffe5caca7e10e4e61c3624eaa0941cd00260801c5b60108216156145d1576fffcb9843d60f6159c9db58835c9266440260801c5b60208216156145f0576fff973b41fa98c081472e6896dfb254c00260801c5b604082161561460f576fff2ea16466c96a3843ec78b326b528610260801c5b608082161561462e576ffe5dee046a99a2a811c461f1969c30530260801c5b61010082161561464e576ffcbe86c7900a88aedcffc83b479aa3a40260801c5b61020082161561466e576ff987a7253ac413176f2b074cf7815e540260801c5b61040082161561468e576ff3392b0822b70005940c7a398e4b70f30260801c5b6108008216156146ae576fe7159475a2c29b7443b29c7fa6e889d90260801c5b6110008216156146ce576fd097f3bdfd2022b8845ad8f792aa58250260801c5b6120008216156146ee576fa9f746462d870fdf8a65dc1f90e061e50260801c5b61400082161561470e576f70d869a156d2a1b890bb3df62baf32f70260801c5b61800082161561472e576f31be135f97d08fd981231505542fcfa60260801c5b6201000082161561474f576f09aa508b5b7a84e1c677de54f3e99bc90260801c5b6202000082161561476f576e5d6af8dedb81196699c329225ee6040260801c5b6204000082161561478e576d2216e584f5fa1ea926041bedfe980260801c5b620800008216156147ab576b048a170391f7dc42444e8fa20260801c5b60008460020b13156147c65780600019816147c257fe5b0490505b6401000000008106156147da5760016147dd565b60005b60ff16602082901c0192505050919050565b6000808060001985870986860292508281109083900303905080614825576000841161481a57600080fd5b508290049050610757565b80841161483157600080fd5b6000848688096000868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b6040518060a001604052806148b1614906565b81526020016148be614906565b81526020016148cb614906565b815260006020820181905260409091015290565b60408051608081018252600080825260208201819052918101829052606081019190915290565b6040518060200160405280600081525090565b60405180606001604052806000815260200160008152602001600081525090565b600082601f83011261494a578081fd5b8151602061495f61495a836152ba565b615297565b828152818101908583018385028701840188101561497b578586fd5b855b858110156149a257815161499081615303565b8452928401929084019060010161497d565b5090979650505050505050565b600082601f8301126149bf578081fd5b815160206149cf61495a836152ba565b82815281810190858301838502870184018810156149eb578586fd5b855b858110156149a2578151845292840192908401906001016149ed565b600082601f830112614a19578081fd5b81516001600160401b03811115614a2c57fe5b614a3f601f8201601f1916602001615297565b818152846020838601011115614a53578283fd5b612d208260208301602087016152d7565b600060408284031215614a75578081fd5b604051604081016001600160401b038282108183111715614a9257fe5b8160405282935084519150614aa682615303565b90825260208401519080821115614abc57600080fd5b50614ac985828601614a09565b6020830152505092915050565b80516001600160501b038116811461055257600080fd5b60008060408385031215614aff578182fd5b8235614b0a81615303565b9150602083013560028110614b1d578182fd5b809150509250929050565b60006020808385031215614b3a578182fd5b82516001600160401b03811115614b4f578283fd5b8301601f81018513614b5f578283fd5b8051614b6d61495a826152ba565b8181528381019083850185840285018601891015614b89578687fd5b8694505b83851015614bb4578051614ba081615303565b835260019490940193918501918501614b8d565b50979650505050505050565b60008060408385031215614bd2578182fd5b82516001600160401b0380821115614be8578384fd5b818501915085601f830112614bfb578384fd5b81516020614c0b61495a836152ba565b82815281810190858301838502870184018b1015614c27578889fd5b8896505b84871015614c575780518060060b8114614c4357898afd5b835260019690960195918301918301614c2b565b5091880151919650909350505080821115614c70578283fd5b50614c7d8582860161493a565b9150509250929050565b60006020808385031215614c99578182fd5b82516001600160401b03811115614cae578283fd5b8301601f81018513614cbe578283fd5b8051614ccc61495a826152ba565b81815283810190838501865b84811015614d0157614cef8a888451890101614a64565b84529286019290860190600101614cd8565b509098975050505050505050565b600060208284031215614d20578081fd5b81516001600160401b03811115614d35578182fd5b612d20848285016149af565b60008060408385031215614d53578182fd5b82516001600160401b0380821115614d69578384fd5b614d75868387016149af565b93506020850151915080821115614d8a578283fd5b50614c7d85828601614a09565b600060208284031215614da8578081fd5b81518015158114610757578182fd5b600060208284031215614dc8578081fd5b5051919050565b600060208284031215614de0578081fd5b81516001600160401b03811115614df5578182fd5b612d2084828501614a64565b600080600080600060a08688031215614e18578081fd5b614e2186614ad6565b9450602086015193506040860151925060608601519150614e4460808701614ad6565b90509295509295909350565b600060208284031215614e61578081fd5b815160ff81168114610757578182fd5b6000815180845260208085019450808401835b83811015614ea057815187529582019590820190600101614e84565b509495945050505050565b60008151808452614ec38160208601602086016152d7565b601f01601f19169290920160200192915050565b60008251614ee98184602087016152d7565b9190910192915050565b6001600160a01b0391909116815260200190565b6001600160a01b0394851681529290931660208301526040820152606081019190915260800190565b6001600160a01b0383168152604060208201819052600090612d2090830184614eab565b6001600160a01b0385168152608060208201819052600090614f7890830186614eab565b64ffffffffff851660408401528281036060840152613a118185614eab565b6001600160a01b03929092168252602082015260400190565b600060408252614fc36040830185614e71565b82810360208401526141cb8185614eab565b600060808252614fe86080830187614e71565b85602084015282810360408401526150008186614e71565b90508281036060840152613a118185614eab565b6020808252825182820181905260009190848201906040850190845b8181101561505257835163ffffffff1683529284019291840191600101615030565b50909695505050505050565b6001600160a01b039687168152948616602086015260408501939093526060840191909152909216608082015260a081019190915260c00190565b90815260200190565b6020808252601d908201527f536561736f6e3a205374696c6c2063757272656e7420536561736f6e2e000000604082015260600190565b6020808252600c908201526b14185a5908084f481bddd95960a21b604082015260600190565b6020808252600f908201526e29b2b0b9b7b71d102830bab9b2b21760891b604082015260600190565b602080825260029082015261042560f41b604082015260600190565b6020808252601c908201527f4f7261636c653a20546f6b656e206e6f7420737570706f727465642e00000000604082015260600190565b6020808252601190820152702132b0b7103737ba1034b7102bb2b6361760791b604082015260600190565b60208082526017908201527629ba34b6361030b1ba34bb32903332b93a34b634bd32b960491b604082015260600190565b6001600160801b0391909116815260200190565b91825260090b602082015260400190565b91825260000b602082015260400190565b9283526020830191909152604082015260600190565b63ffffffff91909116815260200190565b60006040820163ffffffff8516835260206040818501528185518084526060860191508287019350845b818110156149a25784516001600160a01b03168352938301939183019160010161525e565b6001600160501b0391909116815260200190565b6040518181016001600160401b03811182821017156152b257fe5b604052919050565b60006001600160401b038211156152cd57fe5b5060209081020190565b60005b838110156152f25781810151838201526020016152da565b838111156110665750506000910152565b6001600160a01b038116811461531857600080fd5b5056fe783e9dbd7ada0882f5e75946cdc58b9d3ff5bb4329ad861a80e70f123144955453616665436173743a2076616c756520646f65736e27742066697420696e2031323820626974735369676e6564536166654d6174683a206164646974696f6e206f766572666c6f77416464726573733a20696e73756666696369656e742062616c616e636520666f722063616c6c536166654d6174683a206164646974696f6e206f766572666c6f770000000000536166654d6174683a206d756c7469706c69636174696f6e206f766572666c6f77536166654d6174683a207375627472616374696f6e206f766572666c6f77000053616665436173743a2076616c756520646f65736e27742066697420696e20616e20696e743235365369676e6564536166654d6174683a207375627472616374696f6e206f766572666c6f775361666545524332303a204552433230206f7065726174696f6e20646964206e6f742073756363656564a2646970667358221220eedbd91f117a09013ab3562fff2141398d7c9ecc3031ce9cc6362111ba1282ca64736f6c63430007060033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.