Description:
Decentralized Finance (DeFi) protocol contract providing Mintable, Burnable, Swap, Liquidity, Yield, Factory, Oracle functionality.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/periphery/BasicRewardsOracle.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.18;
import {Governance} from "@periphery/utils/Governance.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IStrategyInterface} from "../interfaces/IStrategyInterface.sol";
import {Simulate, IUniswapV3Pool} from "@uniswap-v3-core/libraries/Simulate.sol";
/**
* @title IMorphoGenericOracle
* @notice Interface for Morpho reward oracles
*/
interface IMorphoGenericOracle {
/**
* @notice Get the rewards rate for a vault
* @param _vault The vault to get rewards rate for
* @return The rewards APR as 1e18 (e.g., 10% = 1e17)
*/
function getRewardsRate(address _vault) external view returns (uint256);
}
/**
* @title IOracle
* @notice Interface for price oracles (Chainlink compatible)
*/
interface IOracle {
function latestAnswer() external view returns (int256);
function decimals() external view returns (uint8);
}
/**
* @title BasicRewardsOracle
* @author Your Protocol
* @notice Oracle for calculating reward APR for Morpho compounders including MORPHO rewards
* @dev This oracle uses Uniswap V3 for MORPHO pricing and oracle feeds for other tokens
*/
contract BasicRewardsOracle is IMorphoGenericOracle, Governance {
// ========================================
// ============= STRUCTS ==================
// ========================================
/**
* @notice Reward token configuration
* @param token Address of the reward token
* @param priceOracle Address of the price oracle for this token
* @param rewardRate Reward rate per second in token units (with token decimals)
* @param decimals Token decimals
*/
struct RewardToken {
address token;
address priceOracle;
uint256 rewardRate;
uint8 decimals;
}
/**
* @notice Vault rewards configuration
* @param morphoRate MORPHO tokens per year (set by governance, like morphoRate in MorphoAprOracle)
* @param rewardTokens Array of additional reward tokens for this vault
* @param assetDecimals Decimals of the vault's asset
* @param assetPriceOracle Price oracle for the vault's asset
*/
struct VaultRewards {
uint256 morphoRate; // Annual MORPHO rewards
RewardToken[] rewardTokens;
uint8 assetDecimals;
address assetPriceOracle;
}
// ========================================
// ============= CONSTANTS ================
// ========================================
uint256 public constant WAD = 1e18;
uint256 public constant ORACLE_DECIMALS = 1e8; // Standard oracle decimals (8)
uint256 public constant SECONDS_PER_YEAR = 31_556_952; // Seconds in a year
uint256 public constant MAX_BPS = 10_000;
// Uniswap V3 sqrt price limits
uint160 internal constant MIN_SQRT_RATIO = 4295128739;
uint160 internal constant MAX_SQRT_RATIO =
1461446703485210103287273052203988822378723970342;
// ========================================
// ============= STORAGE ==================
// ========================================
/// @notice Vault rewards configuration
mapping(address => VaultRewards) public vaultRewards;
/// @notice Default oracle decimals (8 for Chainlink on mainnet)
uint8 public defaultOracleDecimals = 8;
/// @notice Global MORPHO pricing configuration
address public morphoToken;
address public morphoWethPool;
IOracle public wethUsdOracle;
uint256 public per = 1e8;
// ========================================
// ============= EVENTS ===================
// ========================================
event VaultRewardsUpdated(
address indexed vault,
uint256 morphoRate,
uint256 rewardTokenCount
);
event MorphoRateUpdated(address indexed vault, uint256 morphoRate);
event RewardTokenAdded(
address indexed vault,
address indexed token,
uint256 rewardRate
);
event RewardTokenRemoved(address indexed vault, address indexed token);
// ========================================
// ============= CONSTRUCTOR ==============
// ========================================
/**
* @notice Constructor
* @param _governance Address of the governance contract
*/
constructor(address _governance) Governance(_governance) {
// Set default MORPHO pricing configuration (Mainnet defaults)
morphoToken = 0x58D97B57BB95320F9a05dC918Aef65434969c2B2; // MORPHO on mainnet
morphoWethPool = 0x25b96761e765b9AC20db18fA57Fa91e3b617Ec6F;
wethUsdOracle = IOracle(0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419);
}
// ========================================
// ========= EXTERNAL FUNCTIONS ===========
// ========================================
/**
* @notice Get the rewards APR for a vault
* @param _vault The vault to get rewards rate for
* @return totalAPR The total rewards APR as 1e18
*/
function getRewardsRate(
address _vault
) external view override returns (uint256 totalAPR) {
VaultRewards storage rewards = vaultRewards[_vault];
// Return 0 if no rewards configured
if (rewards.morphoRate == 0 && rewards.rewardTokens.length == 0) {
return 0;
}
// Calculate the USD value of the vault's TVL (in 1e8 decimals)
uint256 tvlUSD = _getTokenValueUSD(
IStrategyInterface(_vault).totalAssets(),
rewards.assetPriceOracle,
rewards.assetDecimals
);
if (tvlUSD == 0) return 0;
// Calculate MORPHO rewards APR (if configured)
if (rewards.morphoRate > 0) {
uint256 morphoAPR = _getMorphoAPR(rewards.morphoRate);
totalAPR += morphoAPR;
}
// Sum up APR from all additional reward tokens
for (uint256 i = 0; i < rewards.rewardTokens.length; i++) {
RewardToken memory rewardToken = rewards.rewardTokens[i];
if (rewardToken.rewardRate == 0) continue;
uint256 annualRewardAmount = rewardToken.rewardRate *
SECONDS_PER_YEAR;
// Get the USD value of annual rewards (in 1e8 decimals)
uint256 annualRewardUSD = _getTokenValueUSD(
annualRewardAmount,
rewardToken.priceOracle,
rewardToken.decimals
);
// Calculate APR: (annual reward USD / TVL USD) * 1e18
// Both values are in 1e8, so result needs to be scaled to 1e18
uint256 tokenAPR = (annualRewardUSD * WAD) / tvlUSD;
totalAPR += tokenAPR;
}
return totalAPR;
}
// ========================================
// ======= GOVERNANCE FUNCTIONS ===========
// ========================================
/**
* @notice Set complete rewards configuration for a vault
* @param _vault The vault address
* @param _morphoRate Annual MORPHO rewards (like morphoRate in MorphoAprOracle)
* @param _tokens Array of additional reward token addresses
* @param _priceOracles Array of price oracle addresses for additional tokens
* @param _rewardRates Array of reward rates per second for additional tokens
* @param _assetPriceOracle Price oracle for the vault's asset
*/
function setVaultRewards(
address _vault,
uint256 _morphoRate,
address[] calldata _tokens,
address[] calldata _priceOracles,
uint256[] calldata _rewardRates,
address _assetPriceOracle
) external onlyGovernance {
require(_vault != address(0), "Invalid vault");
require(
_tokens.length == _priceOracles.length &&
_tokens.length == _rewardRates.length,
"Array length mismatch"
);
// Clear existing rewards
delete vaultRewards[_vault].rewardTokens;
VaultRewards storage rewards = vaultRewards[_vault];
// Set MORPHO rate
rewards.morphoRate = _morphoRate;
// Set asset configuration
rewards.assetPriceOracle = _assetPriceOracle;
rewards.assetDecimals = IStrategyInterface(IStrategyInterface(_vault).asset()).decimals();
// Add additional reward tokens
for (uint256 i = 0; i < _tokens.length; i++) {
require(_tokens[i] != address(0), "Invalid token");
require(_priceOracles[i] != address(0), "Invalid price oracle");
rewards.rewardTokens.push(
RewardToken({
token: _tokens[i],
priceOracle: _priceOracles[i],
rewardRate: _rewardRates[i],
decimals: ERC20(_tokens[i]).decimals()
})
);
emit RewardTokenAdded(_vault, _tokens[i], _rewardRates[i]);
}
emit VaultRewardsUpdated(_vault, _morphoRate, _tokens.length);
}
/**
* @notice Set global Uniswap V3 pool for MORPHO pricing
* @param _morphoToken MORPHO token address
* @param _morphoWethPool MORPHO/WETH Uniswap V3 pool address
* @param _wethUsdOracle Oracle for WETH/USD pricing
*/
function setMorphoPricing(
address _morphoToken,
address _morphoWethPool,
address _wethUsdOracle
) external onlyGovernance {
require(_morphoToken != address(0), "Invalid token");
require(_morphoWethPool != address(0), "Invalid pool");
require(_wethUsdOracle != address(0), "Invalid oracle");
morphoToken = _morphoToken;
morphoWethPool = _morphoWethPool;
wethUsdOracle = IOracle(_wethUsdOracle);
}
/**
* @notice Update MORPHO rate for a vault
* @param _vault The vault address
* @param _morphoRate New annual MORPHO rewards
*/
function setMorphoRate(
address _vault,
uint256 _morphoRate
) external onlyGovernance {
require(_vault != address(0), "Invalid vault");
vaultRewards[_vault].morphoRate = _morphoRate;
emit MorphoRateUpdated(_vault, _morphoRate);
}
/**
* @notice Update MORPHO rates for multiple vaults
* @param _vaults Array of vault addresses
* @param _morphoRates Array of annual MORPHO rewards
*/
function setMorphoRates(
address[] calldata _vaults,
uint256[] calldata _morphoRates
) external onlyGovernance {
require(_vaults.length == _morphoRates.length, "Array length mismatch");
for (uint256 i = 0; i < _vaults.length; i++) {
require(_vaults[i] != address(0), "Invalid vault");
vaultRewards[_vaults[i]].morphoRate = _morphoRates[i];
emit MorphoRateUpdated(_vaults[i], _morphoRates[i]);
}
}
function setPer(uint256 _per) external onlyGovernance {
per = _per;
}
/**
* @notice Update reward rate for a specific additional token
* @param _vault The vault address
* @param _tokenIndex Index of the token in the rewards array
* @param _rewardRate New reward rate per second in token units
*/
function updateRewardRate(
address _vault,
uint256 _tokenIndex,
uint256 _rewardRate
) external onlyGovernance {
require(_vault != address(0), "Invalid vault");
require(
_tokenIndex < vaultRewards[_vault].rewardTokens.length,
"Invalid token index"
);
vaultRewards[_vault].rewardTokens[_tokenIndex].rewardRate = _rewardRate;
}
/**
* @notice Update price oracle for a reward token
* @param _vault The vault address
* @param _tokenIndex Index of the token in the rewards array
* @param _priceOracle New price oracle address
*/
function updatePriceOracle(
address _vault,
uint256 _tokenIndex,
address _priceOracle
) external onlyGovernance {
require(_vault != address(0), "Invalid vault");
require(
_tokenIndex < vaultRewards[_vault].rewardTokens.length,
"Invalid token index"
);
require(_priceOracle != address(0), "Invalid price oracle");
vaultRewards[_vault]
.rewardTokens[_tokenIndex]
.priceOracle = _priceOracle;
}
/**
* @notice Remove all rewards for a vault
* @param _vault The vault address
*/
function removeVaultRewards(address _vault) external onlyGovernance {
delete vaultRewards[_vault];
emit VaultRewardsUpdated(_vault, 0, 0);
}
// ========================================
// ========== VIEW FUNCTIONS ==============
// ========================================
/**
* @notice Get MORPHO rewards rate for a vault
* @param _vault The vault address
* @return MORPHO rewards APR as 1e18
*/
function getMorphoRewardsRate(
address _vault
) external view returns (uint256) {
VaultRewards storage rewards = vaultRewards[_vault];
if (rewards.morphoRate == 0) return 0;
return _getMorphoAPR(rewards.morphoRate);
}
/**
* @notice Get reward tokens for a vault
* @param _vault The vault address
* @return Array of reward tokens
*/
function getVaultRewardTokens(
address _vault
) external view returns (RewardToken[] memory) {
return vaultRewards[_vault].rewardTokens;
}
/**
* @notice Get vault configuration
* @param _vault The vault address
* @return morphoRate Annual MORPHO rewards
* @return assetPriceOracle Asset price oracle
* @return rewardTokenCount Number of additional reward tokens
*/
function getVaultConfig(
address _vault
)
external
view
returns (
uint256 morphoRate,
address assetPriceOracle,
uint256 rewardTokenCount
)
{
VaultRewards storage rewards = vaultRewards[_vault];
return (
rewards.morphoRate,
rewards.assetPriceOracle,
rewards.rewardTokens.length
);
}
// ========================================
// ======== INTERNAL FUNCTIONS ============
// ========================================
/**
* @notice Calculate MORPHO rewards APR
* @param morphoRate Annual MORPHO rewards
* @return MORPHO APR in 1e18
*/
function _getMorphoAPR(uint256 morphoRate) internal view returns (uint256) {
if (morphoRate == 0 || morphoWethPool == address(0)) return 0;
IUniswapV3Pool pool = IUniswapV3Pool(morphoWethPool);
// Determine swap direction based on token order in the pool
address token0 = pool.token0();
address token1 = pool.token1();
bool zeroForOne;
if (token0 == morphoToken) {
zeroForOne = true; // MORPHO -> WETH
} else if (token1 == morphoToken) {
zeroForOne = false; // WETH <- MORPHO
} else {
return 0; // Invalid pool configuration
}
// Simulate swap to get MORPHO price in WETH
(, int256 wethAmount) = Simulate.simulateSwap(
pool,
zeroForOne,
int256(morphoRate),
zeroForOne ? MIN_SQRT_RATIO + 1 : MAX_SQRT_RATIO - 1
);
// WETH amount should be negative (outgoing) regardless of swap direction
if (wethAmount >= 0) return 0;
// Get WETH price in USD
uint256 wethPrice = _getOraclePrice(address(wethUsdOracle));
if (wethPrice == 0) return 0;
return (uint256(-wethAmount) * wethPrice) / per;
}
/**
* @notice Get USD value of tokens
* @param _amount Token amount with decimals
* @param _priceOracle Price oracle address
* @param _tokenDecimals Token decimals
* @return USD value scaled to 1e8 (standard oracle decimals)
*/
function _getTokenValueUSD(
uint256 _amount,
address _priceOracle,
uint8 _tokenDecimals
) internal view returns (uint256) {
if (_amount == 0) return 0;
// If no price oracle is set, assume 1:1 with USD (for stablecoins)
if (_priceOracle == address(0)) {
// Convert to 1e8 precision
return (_amount * ORACLE_DECIMALS) / (10 ** _tokenDecimals);
}
uint256 price = _getOraclePrice(_priceOracle);
if (price == 0) return 0;
// Convert to 1e8 precision
return (_amount * price) / (10 ** _tokenDecimals);
}
/**
* @notice Get price from oracle
* @param _oracle Oracle address
* @return Price in 8 decimals
*/
function _getOraclePrice(address _oracle) internal view returns (uint256) {
if (_oracle == address(0)) return ORACLE_DECIMALS; // $1 default
try IOracle(_oracle).latestAnswer() returns (int256 price) {
if (price <= 0) return 0;
// Get oracle decimals, fallback to default if not available
uint8 oracleDecimals = _getOracleDecimals(_oracle);
// Normalize to 8 decimals
if (oracleDecimals == 8) {
return uint256(price);
} else if (oracleDecimals < 8) {
return uint256(price) * 10 ** (8 - oracleDecimals);
} else {
return uint256(price) / 10 ** (oracleDecimals - 8);
}
} catch {
return 0;
}
}
/**
* @notice Get oracle decimals with fallback
* @param _oracle Oracle address
* @return decimals Oracle decimals
*/
function _getOracleDecimals(address _oracle) internal view returns (uint8) {
try IOracle(_oracle).decimals() returns (uint8 decimals) {
return decimals;
} catch {
return defaultOracleDecimals;
}
}
}
"
},
"lib/tokenized-strategy-periphery/src/utils/Governance.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;
contract Governance {
/// @notice Emitted when the governance address is updated.
event GovernanceTransferred(
address indexed previousGovernance,
address indexed newGovernance
);
modifier onlyGovernance() {
_checkGovernance();
_;
}
/// @notice Checks if the msg sender is the governance.
function _checkGovernance() internal view virtual {
require(governance == msg.sender, "!governance");
}
/// @notice Address that can set the default base fee and provider
address public governance;
constructor(address _governance) {
governance = _governance;
emit GovernanceTransferred(address(0), _governance);
}
/**
* @notice Sets a new address as the governance of the contract.
* @dev Throws if the caller is not current governance.
* @param _newGovernance The new governance address.
*/
function transferGovernance(
address _newGovernance
) external virtual onlyGovernance {
require(_newGovernance != address(0), "ZERO ADDRESS");
address oldGovernance = governance;
governance = _newGovernance;
emit GovernanceTransferred(oldGovernance, _newGovernance);
}
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}
"
},
"src/interfaces/IStrategyInterface.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.18;
import {IBase4626Compounder} from "@periphery/Bases/4626Compounder/IBase4626Compounder.sol";
interface IStrategyInterface is IBase4626Compounder {}
"
},
"lib/v3-core/contracts/libraries/Simulate.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {SwapMath} from './SwapMath.sol';
import {SafeCast} from './SafeCast.sol';
import {TickMath} from './TickMath.sol';
import {TickBitmap} from './TickBitmap.sol';
import {BitMath} from './BitMath.sol';
import {IUniswapV3Pool} from '../interfaces/IUniswapV3Pool.sol';
/// @title Library for simulating swaps.
/// @notice By fully replicating the swap logic, we can make a static call to get a quote.
library Simulate {
using SafeCast for uint256;
struct Cache {
// price at the beginning of the swap
uint160 sqrtPriceX96Start;
// tick at the beginning of the swap
int24 tickStart;
// liquidity at the beginning of the swap
uint128 liquidityStart;
// the lp fee of the pool
uint24 fee;
// the tick spacing of the pool
int24 tickSpacing;
}
struct State {
// the amount remaining to be swapped in/out of the input/output asset
int256 amountSpecifiedRemaining;
// the amount already swapped out/in of the output/input asset
int256 amountCalculated;
// current sqrt(price)
uint160 sqrtPriceX96;
// the tick associated with the current price
int24 tick;
// the current liquidity in range
uint128 liquidity;
}
// copied from UniswapV3Pool to avoid pragma issues associated with importing it
struct StepComputations {
// the price at the beginning of the step
uint160 sqrtPriceStartX96;
// the next tick to swap to from the current tick in the swap direction
int24 tickNext;
// whether tickNext is initialized or not
bool initialized;
// sqrt(price) for the next tick (1/0)
uint160 sqrtPriceNextX96;
// how much is being swapped in in this step
uint256 amountIn;
// how much is being swapped out
uint256 amountOut;
// how much fee is being paid in
uint256 feeAmount;
}
function simulateSwap(
IUniswapV3Pool pool,
bool zeroForOne,
int256 amountSpecified,
uint160 sqrtPriceLimitX96
) internal view returns (int256 amount0, int256 amount1) {
require(amountSpecified != 0, 'AS');
(uint160 sqrtPriceX96, int24 tick, , , , , ) = pool.slot0();
require(
zeroForOne
? sqrtPriceLimitX96 < sqrtPriceX96 && sqrtPriceLimitX96 > TickMath.MIN_SQRT_RATIO
: sqrtPriceLimitX96 > sqrtPriceX96 && sqrtPriceLimitX96 < TickMath.MAX_SQRT_RATIO,
'SPL'
);
Cache memory cache = Cache({
sqrtPriceX96Start: sqrtPriceX96,
tickStart: tick,
liquidityStart: pool.liquidity(),
fee: pool.fee(),
tickSpacing: pool.tickSpacing()
});
bool exactInput = amountSpecified > 0;
State memory state = State({
amountSpecifiedRemaining: amountSpecified,
amountCalculated: 0,
sqrtPriceX96: cache.sqrtPriceX96Start,
tick: cache.tickStart,
liquidity: cache.liquidityStart
});
while (state.amountSpecifiedRemaining != 0 && state.sqrtPriceX96 != sqrtPriceLimitX96) {
StepComputations memory step;
step.sqrtPriceStartX96 = state.sqrtPriceX96;
(step.tickNext, step.initialized) = nextInitializedTickWithinOneWord(
pool.tickBitmap,
state.tick,
cache.tickSpacing,
zeroForOne
);
if (step.tickNext < TickMath.MIN_TICK) {
step.tickNext = TickMath.MIN_TICK;
} else if (step.tickNext > TickMath.MAX_TICK) {
step.tickNext = TickMath.MAX_TICK;
}
step.sqrtPriceNextX96 = TickMath.getSqrtRatioAtTick(step.tickNext);
(state.sqrtPriceX96, step.amountIn, step.amountOut, step.feeAmount) = SwapMath.computeSwapStep(
state.sqrtPriceX96,
(zeroForOne ? step.sqrtPriceNextX96 < sqrtPriceLimitX96 : step.sqrtPriceNextX96 > sqrtPriceLimitX96)
? sqrtPriceLimitX96
: step.sqrtPriceNextX96,
state.liquidity,
state.amountSpecifiedRemaining,
cache.fee
);
if (exactInput) {
unchecked {
state.amountSpecifiedRemaining -= (step.amountIn + step.feeAmount).toInt256();
}
state.amountCalculated -= step.amountOut.toInt256();
} else {
unchecked {
state.amountSpecifiedRemaining += step.amountOut.toInt256();
}
state.amountCalculated += (step.amountIn + step.feeAmount).toInt256();
}
if (state.sqrtPriceX96 == step.sqrtPriceNextX96) {
if (step.initialized) {
(, int128 liquidityNet, , , , , , ) = pool.ticks(step.tickNext);
unchecked {
if (zeroForOne) liquidityNet = -liquidityNet;
}
state.liquidity = liquidityNet < 0
? state.liquidity - uint128(-liquidityNet)
: state.liquidity + uint128(liquidityNet);
}
unchecked {
state.tick = zeroForOne ? step.tickNext - 1 : step.tickNext;
}
} else if (state.sqrtPriceX96 != step.sqrtPriceStartX96) {
// recompute unless we're on a lower tick boundary (i.e. already transitioned ticks), and haven't moved
state.tick = TickMath.getTickAtSqrtRatio(state.sqrtPriceX96);
}
}
(amount0, amount1) = zeroForOne == exactInput
? (amountSpecified - state.amountSpecifiedRemaining, state.amountCalculated)
: (state.amountCalculated, amountSpecified - state.amountSpecifiedRemaining);
}
// This function replicates TickBitmap, but accepts a function pointer argument.
// It's private because it's messy, and shouldn't be re-used.
function nextInitializedTickWithinOneWord(
function(int16) external view returns (uint256) self,
int24 tick,
int24 tickSpacing,
bool lte
) private view returns (int24 next, bool initialized) {
unchecked {
int24 compressed = tick / tickSpacing;
if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity
if (lte) {
(int16 wordPos, uint8 bitPos) = TickBitmap.position(compressed);
// all the 1s at or to the right of the current bitPos
uint256 mask = (1 << bitPos) - 1 + (1 << bitPos);
uint256 masked = self(wordPos) & mask;
// if there are no initialized ticks to the right of or at the current tick, return rightmost in the word
initialized = masked != 0;
// overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick
next = initialized
? (compressed - int24(uint24(bitPos - BitMath.mostSignificantBit(masked)))) * tickSpacing
: (compressed - int24(uint24(bitPos))) * tickSpacing;
} else {
// start from the word of the next tick, since the current tick state doesn't matter
(int16 wordPos, uint8 bitPos) = TickBitmap.position(compressed + 1);
// all the 1s at or to the left of the bitPos
uint256 mask = ~((1 << bitPos) - 1);
uint256 masked = self(wordPos) & mask;
// if there are no initialized ticks to the left of the current tick, return leftmost in the word
initialized = masked != 0;
// overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick
next = initialized
? (compressed + 1 + int24(uint24(BitMath.leastSignificantBit(masked) - bitPos))) * tickSpacing
: (compressed + 1 + int24(uint24(type(uint8).max - bitPos))) * tickSpacing;
}
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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);
/**
* @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 `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, 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 `from` to `to` 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 from, address to, uint256 amount) external returns (bool);
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
"
},
"lib/openzeppelin-contracts/contracts/utils/Context.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
"
},
"lib/tokenized-strategy-periphery/src/Bases/4626Compounder/IBase4626Compounder.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;
import {IBaseHealthCheck} from "../HealthCheck/IBaseHealthCheck.sol";
interface IBase4626Compounder is IBaseHealthCheck {
function vault() external view returns (address);
function balanceOfAsset() external view returns (uint256);
function balanceOfVault() external view returns (uint256);
function balanceOfStake() external view returns (uint256);
function valueOfVault() external view returns (uint256);
function vaultsMaxWithdraw() external view returns (uint256);
}
"
},
"lib/v3-core/contracts/libraries/SwapMath.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
import {FullMath} from './FullMath.sol';
import {SqrtPriceMath} from './SqrtPriceMath.sol';
/// @title Computes the result of a swap within ticks
/// @notice Contains methods for computing the result of a swap within a single tick price range, i.e., a single tick.
library SwapMath {
/// @notice Computes the result of swapping some amount in, or amount out, given the parameters of the swap
/// @dev The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive
/// @param sqrtRatioCurrentX96 The current sqrt price of the pool
/// @param sqrtRatioTargetX96 The price that cannot be exceeded, from which the direction of the swap is inferred
/// @param liquidity The usable liquidity
/// @param amountRemaining How much input or output amount is remaining to be swapped in/out
/// @param feePips The fee taken from the input amount, expressed in hundredths of a bip
/// @return sqrtRatioNextX96 The price after swapping the amount in/out, not to exceed the price target
/// @return amountIn The amount to be swapped in, of either token0 or token1, based on the direction of the swap
/// @return amountOut The amount to be received, of either token0 or token1, based on the direction of the swap
/// @return feeAmount The amount of input that will be taken as a fee
function computeSwapStep(
uint160 sqrtRatioCurrentX96,
uint160 sqrtRatioTargetX96,
uint128 liquidity,
int256 amountRemaining,
uint24 feePips
)
internal
pure
returns (
uint160 sqrtRatioNextX96,
uint256 amountIn,
uint256 amountOut,
uint256 feeAmount
)
{
unchecked {
bool zeroForOne = sqrtRatioCurrentX96 >= sqrtRatioTargetX96;
bool exactIn = amountRemaining >= 0;
if (exactIn) {
uint256 amountRemainingLessFee = FullMath.mulDiv(uint256(amountRemaining), 1e6 - feePips, 1e6);
amountIn = zeroForOne
? SqrtPriceMath.getAmount0Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, true)
: SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, true);
if (amountRemainingLessFee >= amountIn) sqrtRatioNextX96 = sqrtRatioTargetX96;
else
sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromInput(
sqrtRatioCurrentX96,
liquidity,
amountRemainingLessFee,
zeroForOne
);
} else {
amountOut = zeroForOne
? SqrtPriceMath.getAmount1Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, false)
: SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, false);
if (uint256(-amountRemaining) >= amountOut) sqrtRatioNextX96 = sqrtRatioTargetX96;
else
sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromOutput(
sqrtRatioCurrentX96,
liquidity,
uint256(-amountRemaining),
zeroForOne
);
}
bool max = sqrtRatioTargetX96 == sqrtRatioNextX96;
// get the input/output amounts
if (zeroForOne) {
amountIn = max && exactIn
? amountIn
: SqrtPriceMath.getAmount0Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, true);
amountOut = max && !exactIn
? amountOut
: SqrtPriceMath.getAmount1Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, false);
} else {
amountIn = max && exactIn
? amountIn
: SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, true);
amountOut = max && !exactIn
? amountOut
: SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, false);
}
// cap the output amount to not exceed the remaining output amount
if (!exactIn && amountOut > uint256(-amountRemaining)) {
amountOut = uint256(-amountRemaining);
}
if (exactIn && sqrtRatioNextX96 != sqrtRatioTargetX96) {
// we didn't reach the target, so take the remainder of the maximum input as fee
feeAmount = uint256(amountRemaining) - amountIn;
} else {
feeAmount = FullMath.mulDivRoundingUp(amountIn, feePips, 1e6 - feePips);
}
}
}
}
"
},
"lib/v3-core/contracts/libraries/SafeCast.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Safe casting methods
/// @notice Contains methods for safely casting between types
library SafeCast {
/// @notice Cast a uint256 to a uint160, revert on overflow
/// @param y The uint256 to be downcasted
/// @return z The downcasted integer, now type uint160
function toUint160(uint256 y) internal pure returns (uint160 z) {
require((z = uint160(y)) == y);
}
/// @notice Cast a int256 to a int128, revert on overflow or underflow
/// @param y The int256 to be downcasted
/// @return z The downcasted integer, now type int128
function toInt128(int256 y) internal pure returns (int128 z) {
require((z = int128(y)) == y);
}
/// @notice Cast a uint256 to a int256, revert on overflow
/// @param y The uint256 to be casted
/// @return z The casted integer, now type int256
function toInt256(uint256 y) internal pure returns (int256 z) {
require(y < 2**255);
z = int256(y);
}
}
"
},
"lib/v3-core/contracts/libraries/TickMath.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^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 {
error T();
error R();
/// @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) {
unchecked {
uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));
if (absTick > uint256(int256(MAX_TICK))) revert 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) {
unchecked {
// second inequality must be < because the price can never reach the price at the max tick
if (!(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO)) revert 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;
}
}
}
"
},
"lib/v3-core/contracts/libraries/TickBitmap.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
import {BitMath} from './BitMath.sol';
/// @title Packed tick initialized state library
/// @notice Stores a packed mapping of tick index to its initialized state
/// @dev The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word.
library TickBitmap {
/// @notice Computes the position in the mapping where the initialized bit for a tick lives
/// @param tick The tick for which to compute the position
/// @return wordPos The key in the mapping containing the word in which the bit is stored
/// @return bitPos The bit position in the word where the flag is stored
function position(int24 tick) internal pure returns (int16 wordPos, uint8 bitPos) {
unchecked {
wordPos = int16(tick >> 8);
bitPos = uint8(int8(tick % 256));
}
}
/// @notice Flips the initialized state for a given tick from false to true, or vice versa
/// @param self The mapping in which to flip the tick
/// @param tick The tick to flip
/// @param tickSpacing The spacing between usable ticks
function flipTick(
mapping(int16 => uint256) storage self,
int24 tick,
int24 tickSpacing
) internal {
unchecked {
require(tick % tickSpacing == 0); // ensure that the tick is spaced
(int16 wordPos, uint8 bitPos) = position(tick / tickSpacing);
uint256 mask = 1 << bitPos;
self[wordPos] ^= mask;
}
}
/// @notice Returns the next initialized tick contained in the same word (or
Submitted on: 2025-10-10 21:30:32
Comments
Log in to comment.
No comments yet.