Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/ATokenVault.sol": {
"content": "// SPDX-License-Identifier: UNLICENSED
// All Rights Reserved © AaveCo
pragma solidity ^0.8.10;
import {ERC4626Upgradeable} from "@openzeppelin-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin-upgradeable/access/OwnableUpgradeable.sol";
import {SafeERC20Upgradeable} from "@openzeppelin-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import {IERC20Upgradeable} from "@openzeppelin-upgradeable/interfaces/IERC20Upgradeable.sol";
import {EIP712Upgradeable} from "@openzeppelin-upgradeable/utils/cryptography/EIP712Upgradeable.sol";
import {MathUpgradeable} from "@openzeppelin-upgradeable/utils/math/MathUpgradeable.sol";
import {IncentivizedERC20} from "@aave-v3-core/protocol/tokenization/base/IncentivizedERC20.sol";
import {IPoolAddressesProvider} from "@aave-v3-core/interfaces/IPoolAddressesProvider.sol";
import {IPool} from "@aave-v3-core/interfaces/IPool.sol";
import {IAToken} from "@aave-v3-core/interfaces/IAToken.sol";
import {DataTypes as AaveDataTypes} from "@aave-v3-core/protocol/libraries/types/DataTypes.sol";
import {WadRayMath} from "@aave-v3-core/protocol/libraries/math/WadRayMath.sol";
import {IRewardsController} from "@aave-v3-periphery/rewards/interfaces/IRewardsController.sol";
import {IATokenVault} from "./interfaces/IATokenVault.sol";
import {MetaTxHelpers} from "./libraries/MetaTxHelpers.sol";
import "./libraries/Constants.sol";
import {ATokenVaultStorage} from "./ATokenVaultStorage.sol";
/**
* @title ATokenVault
* @author Aave Protocol
* @notice An ERC-4626 vault for Aave V3, with support to add a fee on yield earned.
*/
contract ATokenVault is ERC4626Upgradeable, OwnableUpgradeable, EIP712Upgradeable, ATokenVaultStorage, IATokenVault {
using SafeERC20Upgradeable for IERC20Upgradeable;
using MathUpgradeable for uint256;
/// @inheritdoc IATokenVault
IPoolAddressesProvider public immutable POOL_ADDRESSES_PROVIDER;
/// @inheritdoc IATokenVault
IPool public immutable AAVE_POOL;
/// @inheritdoc IATokenVault
IAToken public immutable ATOKEN;
/// @inheritdoc IATokenVault
IERC20Upgradeable public immutable UNDERLYING;
/// @inheritdoc IATokenVault
uint16 public immutable REFERRAL_CODE;
/**
* @dev Constructor.
* @param underlying The underlying ERC20 asset which can be supplied to Aave
* @param referralCode The Aave referral code to use for deposits from this vault
* @param poolAddressesProvider The address of the Aave v3 Pool Addresses Provider
*/
constructor(address underlying, uint16 referralCode, IPoolAddressesProvider poolAddressesProvider) {
_disableInitializers();
POOL_ADDRESSES_PROVIDER = poolAddressesProvider;
AAVE_POOL = IPool(poolAddressesProvider.getPool());
REFERRAL_CODE = referralCode;
UNDERLYING = IERC20Upgradeable(underlying);
address aTokenAddress = AAVE_POOL.getReserveData(address(underlying)).aTokenAddress;
require(aTokenAddress != address(0), "ASSET_NOT_SUPPORTED");
ATOKEN = IAToken(aTokenAddress);
}
/**
* @notice Initializes the vault, setting the initial parameters and initializing inherited contracts.
* @dev It requires an initial non-zero deposit to prevent a frontrunning attack (in underlying tokens). Note
* that care should be taken to provide a non-trivial amount, but this depends on the underlying asset's decimals.
* @dev It does not initialize the OwnableUpgradeable contract to avoid setting the proxy admin as the owner.
* @param owner The owner to set
* @param initialFee The initial fee to set, expressed in wad, where 1e18 is 100%
* @param shareName The name to set for this vault
* @param shareSymbol The symbol to set for this vault
* @param initialLockDeposit The initial amount of underlying assets to deposit
*/
function initialize(
address owner,
uint256 initialFee,
string memory shareName,
string memory shareSymbol,
uint256 initialLockDeposit
) external initializer {
require(owner != address(0), "ZERO_ADDRESS_NOT_VALID");
require(initialLockDeposit != 0, "ZERO_INITIAL_LOCK_DEPOSIT");
_transferOwnership(owner);
__ERC4626_init(UNDERLYING);
__ERC20_init(shareName, shareSymbol);
__EIP712_init(shareName, "1");
_setFee(initialFee);
UNDERLYING.safeApprove(address(AAVE_POOL), type(uint256).max);
_handleDeposit(initialLockDeposit, address(this), msg.sender, false);
}
/*//////////////////////////////////////////////////////////////
DEPOSIT/WITHDRAWAL LOGIC
//////////////////////////////////////////////////////////////*/
/// @inheritdoc IATokenVault
function deposit(uint256 assets, address receiver) public override(ERC4626Upgradeable, IATokenVault) returns (uint256) {
return _handleDeposit(assets, receiver, msg.sender, false);
}
/// @inheritdoc IATokenVault
function depositATokens(uint256 assets, address receiver) public override returns (uint256) {
return _handleDeposit(assets, receiver, msg.sender, true);
}
/// @inheritdoc IATokenVault
function depositWithSig(
uint256 assets,
address receiver,
address depositor,
EIP712Signature calldata sig
) public override returns (uint256) {
unchecked {
MetaTxHelpers._validateRecoveredAddress(
MetaTxHelpers._calculateDigest(
keccak256(
abi.encode(
DEPOSIT_WITH_SIG_TYPEHASH,
assets,
receiver,
depositor,
_sigNonces[depositor]++,
sig.deadline
)
),
_domainSeparatorV4()
),
depositor,
sig
);
}
return _handleDeposit(assets, receiver, depositor, false);
}
/// @inheritdoc IATokenVault
function depositATokensWithSig(
uint256 assets,
address receiver,
address depositor,
EIP712Signature calldata sig
) public override returns (uint256) {
unchecked {
MetaTxHelpers._validateRecoveredAddress(
MetaTxHelpers._calculateDigest(
keccak256(
abi.encode(
DEPOSIT_ATOKENS_WITH_SIG_TYPEHASH,
assets,
receiver,
depositor,
_sigNonces[depositor]++,
sig.deadline
)
),
_domainSeparatorV4()
),
depositor,
sig
);
}
return _handleDeposit(assets, receiver, depositor, true);
}
/// @inheritdoc IATokenVault
function mint(uint256 shares, address receiver) public override(ERC4626Upgradeable, IATokenVault) returns (uint256) {
return _handleMint(shares, receiver, msg.sender, false);
}
/// @inheritdoc IATokenVault
function mintWithATokens(uint256 shares, address receiver) public override returns (uint256) {
return _handleMint(shares, receiver, msg.sender, true);
}
/// @inheritdoc IATokenVault
function mintWithSig(
uint256 shares,
address receiver,
address depositor,
EIP712Signature calldata sig
) public override returns (uint256) {
unchecked {
MetaTxHelpers._validateRecoveredAddress(
MetaTxHelpers._calculateDigest(
keccak256(
abi.encode(MINT_WITH_SIG_TYPEHASH, shares, receiver, depositor, _sigNonces[depositor]++, sig.deadline)
),
_domainSeparatorV4()
),
depositor,
sig
);
}
return _handleMint(shares, receiver, depositor, false);
}
/// @inheritdoc IATokenVault
function mintWithATokensWithSig(
uint256 shares,
address receiver,
address depositor,
EIP712Signature calldata sig
) public override returns (uint256) {
unchecked {
MetaTxHelpers._validateRecoveredAddress(
MetaTxHelpers._calculateDigest(
keccak256(
abi.encode(
MINT_WITH_ATOKENS_WITH_SIG_TYPEHASH,
shares,
receiver,
depositor,
_sigNonces[depositor]++,
sig.deadline
)
),
_domainSeparatorV4()
),
depositor,
sig
);
}
return _handleMint(shares, receiver, depositor, true);
}
/// @inheritdoc IATokenVault
function withdraw(
uint256 assets,
address receiver,
address owner
) public override(ERC4626Upgradeable, IATokenVault) returns (uint256) {
return _handleWithdraw(assets, receiver, owner, msg.sender, false);
}
/// @inheritdoc IATokenVault
function withdrawATokens(uint256 assets, address receiver, address owner) public override returns (uint256) {
return _handleWithdraw(assets, receiver, owner, msg.sender, true);
}
/// @inheritdoc IATokenVault
function withdrawWithSig(
uint256 assets,
address receiver,
address owner,
EIP712Signature calldata sig
) public override returns (uint256) {
unchecked {
MetaTxHelpers._validateRecoveredAddress(
MetaTxHelpers._calculateDigest(
keccak256(
abi.encode(WITHDRAW_WITH_SIG_TYPEHASH, assets, receiver, owner, _sigNonces[owner]++, sig.deadline)
),
_domainSeparatorV4()
),
owner,
sig
);
}
return _handleWithdraw(assets, receiver, owner, owner, false);
}
/// @inheritdoc IATokenVault
function withdrawATokensWithSig(
uint256 assets,
address receiver,
address owner,
EIP712Signature calldata sig
) public override returns (uint256) {
unchecked {
MetaTxHelpers._validateRecoveredAddress(
MetaTxHelpers._calculateDigest(
keccak256(
abi.encode(
WITHDRAW_ATOKENS_WITH_SIG_TYPEHASH,
assets,
receiver,
owner,
_sigNonces[owner]++,
sig.deadline
)
),
_domainSeparatorV4()
),
owner,
sig
);
}
return _handleWithdraw(assets, receiver, owner, owner, true);
}
/// @inheritdoc IATokenVault
function redeem(
uint256 shares,
address receiver,
address owner
) public override(ERC4626Upgradeable, IATokenVault) returns (uint256) {
return _handleRedeem(shares, receiver, owner, msg.sender, false);
}
/// @inheritdoc IATokenVault
function redeemAsATokens(uint256 shares, address receiver, address owner) public override returns (uint256) {
return _handleRedeem(shares, receiver, owner, msg.sender, true);
}
/// @inheritdoc IATokenVault
function redeemWithSig(
uint256 shares,
address receiver,
address owner,
EIP712Signature calldata sig
) public override returns (uint256) {
unchecked {
MetaTxHelpers._validateRecoveredAddress(
MetaTxHelpers._calculateDigest(
keccak256(abi.encode(REDEEM_WITH_SIG_TYPEHASH, shares, receiver, owner, _sigNonces[owner]++, sig.deadline)),
_domainSeparatorV4()
),
owner,
sig
);
}
return _handleRedeem(shares, receiver, owner, owner, false);
}
/// @inheritdoc IATokenVault
function redeemWithATokensWithSig(
uint256 shares,
address receiver,
address owner,
EIP712Signature calldata sig
) public override returns (uint256) {
unchecked {
MetaTxHelpers._validateRecoveredAddress(
MetaTxHelpers._calculateDigest(
keccak256(
abi.encode(
REDEEM_WITH_ATOKENS_WITH_SIG_TYPEHASH,
shares,
receiver,
owner,
_sigNonces[owner]++,
sig.deadline
)
),
_domainSeparatorV4()
),
owner,
sig
);
}
return _handleRedeem(shares, receiver, owner, owner, true);
}
/// @inheritdoc IATokenVault
function maxDeposit(address) public view override(ERC4626Upgradeable, IATokenVault) returns (uint256) {
return _maxAssetsSuppliableToAave();
}
/// @inheritdoc IATokenVault
function maxMint(address) public view override(ERC4626Upgradeable, IATokenVault) returns (uint256) {
return _convertToShares(_maxAssetsSuppliableToAave(), MathUpgradeable.Rounding.Down);
}
/// @inheritdoc IATokenVault
function maxWithdraw(address owner) public view override(ERC4626Upgradeable, IATokenVault) returns (uint256) {
uint256 maxWithdrawable = _maxAssetsWithdrawableFromAave();
return
maxWithdrawable == 0 ? 0 : maxWithdrawable.min(_convertToAssets(balanceOf(owner), MathUpgradeable.Rounding.Down));
}
/// @inheritdoc IATokenVault
function maxRedeem(address owner) public view override(ERC4626Upgradeable, IATokenVault) returns (uint256) {
uint256 maxWithdrawable = _maxAssetsWithdrawableFromAave();
return
maxWithdrawable == 0 ? 0 : _convertToShares(maxWithdrawable, MathUpgradeable.Rounding.Down).min(balanceOf(owner));
}
/// @inheritdoc IATokenVault
function previewDeposit(uint256 assets) public view override(ERC4626Upgradeable, IATokenVault) returns (uint256) {
return _convertToShares(_maxAssetsSuppliableToAave().min(assets), MathUpgradeable.Rounding.Down);
}
/// @inheritdoc IATokenVault
function previewMint(uint256 shares) public view override(ERC4626Upgradeable, IATokenVault) returns (uint256) {
return _convertToAssets(shares, MathUpgradeable.Rounding.Up).min(_maxAssetsSuppliableToAave());
}
/// @inheritdoc IATokenVault
function previewWithdraw(uint256 assets) public view override(ERC4626Upgradeable, IATokenVault) returns (uint256) {
uint256 maxWithdrawable = _maxAssetsWithdrawableFromAave();
return maxWithdrawable == 0 ? 0 : _convertToShares(maxWithdrawable.min(assets), MathUpgradeable.Rounding.Up);
}
/// @inheritdoc IATokenVault
function previewRedeem(uint256 shares) public view override(ERC4626Upgradeable, IATokenVault) returns (uint256) {
uint256 maxWithdrawable = _maxAssetsWithdrawableFromAave();
return maxWithdrawable == 0 ? 0 : _convertToAssets(shares, MathUpgradeable.Rounding.Down).min(maxWithdrawable);
}
/// @inheritdoc IATokenVault
function domainSeparator() public view override returns (bytes32) {
return _domainSeparatorV4();
}
/*//////////////////////////////////////////////////////////////
ONLY OWNER FUNCTIONS
//////////////////////////////////////////////////////////////*/
/// @inheritdoc IATokenVault
function setFee(uint256 newFee) public override onlyOwner {
_accrueYield();
_setFee(newFee);
}
/// @inheritdoc IATokenVault
function withdrawFees(address to, uint256 amount) public override onlyOwner {
_accrueYield();
require(amount <= _s.accumulatedFees, "INSUFFICIENT_FEES"); // will underflow below anyway, error msg for clarity
_s.accumulatedFees -= uint128(amount);
ATOKEN.transfer(to, amount);
_s.lastVaultBalance = uint128(ATOKEN.balanceOf(address(this)));
emit FeesWithdrawn(to, amount, _s.lastVaultBalance, _s.accumulatedFees);
}
/// @inheritdoc IATokenVault
function claimRewards(address to) public override onlyOwner {
require(to != address(0), "CANNOT_CLAIM_TO_ZERO_ADDRESS");
address[] memory assets = new address[](1);
assets[0] = address(ATOKEN);
(address[] memory rewardsList, uint256[] memory claimedAmounts) = IRewardsController(
address(IncentivizedERC20(address(ATOKEN)).getIncentivesController())
).claimAllRewards(assets, to);
emit RewardsClaimed(to, rewardsList, claimedAmounts);
}
/// @inheritdoc IATokenVault
function emergencyRescue(address token, address to, uint256 amount) public override onlyOwner {
require(token != address(ATOKEN), "CANNOT_RESCUE_ATOKEN");
IERC20Upgradeable(token).safeTransfer(to, amount);
emit EmergencyRescue(token, to, amount);
}
/*//////////////////////////////////////////////////////////////
VIEW FUNCTIONS
//////////////////////////////////////////////////////////////*/
/// @inheritdoc IATokenVault
function totalAssets() public view override(ERC4626Upgradeable, IATokenVault) returns (uint256) {
// Report only the total assets net of fees, for vault share logic
return ATOKEN.balanceOf(address(this)) - getClaimableFees();
}
/// @inheritdoc IATokenVault
function getClaimableFees() public view override returns (uint256) {
uint256 newVaultBalance = ATOKEN.balanceOf(address(this));
// Skip computation if there is no yield
if (newVaultBalance <= _s.lastVaultBalance) {
return _s.accumulatedFees;
}
uint256 newYield = newVaultBalance - _s.lastVaultBalance;
uint256 newFees = newYield.mulDiv(_s.fee, SCALE, MathUpgradeable.Rounding.Down);
return _s.accumulatedFees + newFees;
}
/// @inheritdoc IATokenVault
function getSigNonce(address signer) public view override returns (uint256) {
return _sigNonces[signer];
}
/// @inheritdoc IATokenVault
function getLastVaultBalance() public view override returns (uint256) {
return _s.lastVaultBalance;
}
/// @inheritdoc IATokenVault
function getFee() public view override returns (uint256) {
return _s.fee;
}
/*//////////////////////////////////////////////////////////////
INTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////*/
function _setFee(uint256 newFee) internal {
require(newFee <= SCALE, "FEE_TOO_HIGH");
uint256 oldFee = _s.fee;
_s.fee = uint64(newFee);
emit FeeUpdated(oldFee, newFee);
}
function _accrueYield() internal {
uint256 newVaultBalance = ATOKEN.balanceOf(address(this));
// Skip computation if there is no yield
if (newVaultBalance <= _s.lastVaultBalance) {
return;
}
uint256 newYield = newVaultBalance - _s.lastVaultBalance;
uint256 newFeesEarned = newYield.mulDiv(_s.fee, SCALE, MathUpgradeable.Rounding.Down);
_s.accumulatedFees += uint128(newFeesEarned);
_s.lastVaultBalance = uint128(newVaultBalance);
emit YieldAccrued(newYield, newFeesEarned, newVaultBalance);
}
function _handleDeposit(uint256 assets, address receiver, address depositor, bool asAToken) internal returns (uint256) {
if (!asAToken) require(assets <= maxDeposit(receiver), "DEPOSIT_EXCEEDS_MAX");
_accrueYield();
uint256 shares = super.previewDeposit(assets);
require(shares != 0, "ZERO_SHARES"); // Check for rounding error since we round down in previewDeposit.
_baseDeposit(_convertToAssets(shares, MathUpgradeable.Rounding.Up), shares, depositor, receiver, asAToken);
return shares;
}
function _handleMint(uint256 shares, address receiver, address depositor, bool asAToken) internal returns (uint256) {
if (!asAToken) require(shares <= maxMint(receiver), "MINT_EXCEEDS_MAX");
_accrueYield();
uint256 assets = super.previewMint(shares); // No need to check for rounding error, previewMint rounds up.
_baseDeposit(assets, shares, depositor, receiver, asAToken);
return assets;
}
function _handleWithdraw(
uint256 assets,
address receiver,
address owner,
address allowanceTarget,
bool asAToken
) internal returns (uint256) {
_accrueYield();
require(assets <= maxWithdraw(owner), "WITHDRAW_EXCEEDS_MAX");
uint256 shares = super.previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.
_baseWithdraw(assets, shares, owner, receiver, allowanceTarget, asAToken);
return shares;
}
function _handleRedeem(
uint256 shares,
address receiver,
address owner,
address allowanceTarget,
bool asAToken
) internal returns (uint256) {
_accrueYield();
require(shares <= maxRedeem(owner), "REDEEM_EXCEEDS_MAX");
uint256 assets = super.previewRedeem(shares);
require(assets != 0, "ZERO_ASSETS"); // Check for rounding error since we round down in previewRedeem.
_baseWithdraw(assets, shares, owner, receiver, allowanceTarget, asAToken);
return assets;
}
function _maxAssetsSuppliableToAave() internal view returns (uint256) {
// returns 0 if reserve is not active, frozen, or paused
// returns max uint256 value if supply cap is 0 (not capped)
// returns supply cap - current amount supplied as max suppliable if there is a supply cap for this reserve
AaveDataTypes.ReserveData memory reserveData = AAVE_POOL.getReserveData(address(UNDERLYING));
uint256 reserveConfigMap = reserveData.configuration.data;
uint256 supplyCap = (reserveConfigMap & ~AAVE_SUPPLY_CAP_MASK) >> AAVE_SUPPLY_CAP_BIT_POSITION;
if (
(reserveConfigMap & ~AAVE_ACTIVE_MASK == 0) ||
(reserveConfigMap & ~AAVE_FROZEN_MASK != 0) ||
(reserveConfigMap & ~AAVE_PAUSED_MASK != 0)
) {
return 0;
} else if (supplyCap == 0) {
return type(uint256).max;
} else {
// Reserve's supply cap - current amount supplied
// See similar logic in Aave v3 ValidationLogic library, in the validateSupply function
// https://github.com/aave/aave-v3-core/blob/a00f28e3ad7c0e4a369d8e06e0ac9fd0acabcab7/contracts/protocol/libraries/logic/ValidationLogic.sol#L71-L78
uint256 currentSupply = WadRayMath.rayMul(
(ATOKEN.scaledTotalSupply() + uint256(reserveData.accruedToTreasury)),
reserveData.liquidityIndex
);
uint256 supplyCapWithDecimals = supplyCap * 10 ** decimals();
return supplyCapWithDecimals > currentSupply ? supplyCapWithDecimals - currentSupply : 0;
}
}
function _maxAssetsWithdrawableFromAave() internal view returns (uint256) {
// returns 0 if reserve is not active, or paused
// otherwise, returns available liquidity
AaveDataTypes.ReserveData memory reserveData = AAVE_POOL.getReserveData(address(UNDERLYING));
uint256 reserveConfigMap = reserveData.configuration.data;
if ((reserveConfigMap & ~AAVE_ACTIVE_MASK == 0) || (reserveConfigMap & ~AAVE_PAUSED_MASK != 0)) {
return 0;
} else {
return UNDERLYING.balanceOf(address(ATOKEN));
}
}
function _baseDeposit(uint256 assets, uint256 shares, address depositor, address receiver, bool asAToken) private {
// Need to transfer before minting or ERC777s could reenter.
if (asAToken) {
ATOKEN.transferFrom(depositor, address(this), assets);
} else {
UNDERLYING.safeTransferFrom(depositor, address(this), assets);
AAVE_POOL.supply(address(UNDERLYING), assets, address(this), REFERRAL_CODE);
}
_s.lastVaultBalance = uint128(ATOKEN.balanceOf(address(this)));
_mint(receiver, shares);
emit Deposit(depositor, receiver, assets, shares);
}
function _baseWithdraw(
uint256 assets,
uint256 shares,
address owner,
address receiver,
address allowanceTarget,
bool asAToken
) private {
if (allowanceTarget != owner) {
_spendAllowance(owner, allowanceTarget, shares);
}
_burn(owner, shares);
// Withdraw assets from Aave v3 and send to receiver
if (asAToken) {
ATOKEN.transfer(receiver, assets);
} else {
AAVE_POOL.withdraw(address(UNDERLYING), assets, receiver);
}
_s.lastVaultBalance = uint128(ATOKEN.balanceOf(address(this)));
emit Withdraw(allowanceTarget, receiver, owner, assets, shares);
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/ERC4626Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.1) (token/ERC20/extensions/ERC4626.sol)
pragma solidity ^0.8.0;
import "../ERC20Upgradeable.sol";
import "../utils/SafeERC20Upgradeable.sol";
import "../../../interfaces/IERC4626Upgradeable.sol";
import "../../../utils/math/MathUpgradeable.sol";
import "../../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in
* https://eips.ethereum.org/EIPS/eip-4626[EIP-4626].
*
* This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for
* underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends
* the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this
* contract and not the "assets" token which is an independent contract.
*
* CAUTION: When the vault is empty or nearly empty, deposits are at high risk of being stolen through frontrunning with
* a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation
* attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial
* deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may
* similarly be affected by slippage. Users can protect against this attack as well unexpected slippage in general by
* verifying the amount received is as expected, using a wrapper that performs these checks such as
* https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router].
*
* _Available since v4.7._
*/
abstract contract ERC4626Upgradeable is Initializable, ERC20Upgradeable, IERC4626Upgradeable {
using MathUpgradeable for uint256;
IERC20Upgradeable private _asset;
uint8 private _decimals;
/**
* @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777).
*/
function __ERC4626_init(IERC20Upgradeable asset_) internal onlyInitializing {
__ERC4626_init_unchained(asset_);
}
function __ERC4626_init_unchained(IERC20Upgradeable asset_) internal onlyInitializing {
(bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_);
_decimals = success ? assetDecimals : super.decimals();
_asset = asset_;
}
/**
* @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way.
*/
function _tryGetAssetDecimals(IERC20Upgradeable asset_) private view returns (bool, uint8) {
(bool success, bytes memory encodedDecimals) = address(asset_).staticcall(
abi.encodeWithSelector(IERC20MetadataUpgradeable.decimals.selector)
);
if (success && encodedDecimals.length >= 32) {
uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256));
if (returnedDecimals <= type(uint8).max) {
return (true, uint8(returnedDecimals));
}
}
return (false, 0);
}
/**
* @dev Decimals are read from the underlying asset in the constructor and cached. If this fails (e.g., the asset
* has not been created yet), the cached value is set to a default obtained by `super.decimals()` (which depends on
* inheritance but is most likely 18). Override this function in order to set a guaranteed hardcoded value.
* See {IERC20Metadata-decimals}.
*/
function decimals() public view virtual override(IERC20MetadataUpgradeable, ERC20Upgradeable) returns (uint8) {
return _decimals;
}
/** @dev See {IERC4626-asset}. */
function asset() public view virtual override returns (address) {
return address(_asset);
}
/** @dev See {IERC4626-totalAssets}. */
function totalAssets() public view virtual override returns (uint256) {
return _asset.balanceOf(address(this));
}
/** @dev See {IERC4626-convertToShares}. */
function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) {
return _convertToShares(assets, MathUpgradeable.Rounding.Down);
}
/** @dev See {IERC4626-convertToAssets}. */
function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) {
return _convertToAssets(shares, MathUpgradeable.Rounding.Down);
}
/** @dev See {IERC4626-maxDeposit}. */
function maxDeposit(address) public view virtual override returns (uint256) {
return _isVaultCollateralized() ? type(uint256).max : 0;
}
/** @dev See {IERC4626-maxMint}. */
function maxMint(address) public view virtual override returns (uint256) {
return type(uint256).max;
}
/** @dev See {IERC4626-maxWithdraw}. */
function maxWithdraw(address owner) public view virtual override returns (uint256) {
return _convertToAssets(balanceOf(owner), MathUpgradeable.Rounding.Down);
}
/** @dev See {IERC4626-maxRedeem}. */
function maxRedeem(address owner) public view virtual override returns (uint256) {
return balanceOf(owner);
}
/** @dev See {IERC4626-previewDeposit}. */
function previewDeposit(uint256 assets) public view virtual override returns (uint256) {
return _convertToShares(assets, MathUpgradeable.Rounding.Down);
}
/** @dev See {IERC4626-previewMint}. */
function previewMint(uint256 shares) public view virtual override returns (uint256) {
return _convertToAssets(shares, MathUpgradeable.Rounding.Up);
}
/** @dev See {IERC4626-previewWithdraw}. */
function previewWithdraw(uint256 assets) public view virtual override returns (uint256) {
return _convertToShares(assets, MathUpgradeable.Rounding.Up);
}
/** @dev See {IERC4626-previewRedeem}. */
function previewRedeem(uint256 shares) public view virtual override returns (uint256) {
return _convertToAssets(shares, MathUpgradeable.Rounding.Down);
}
/** @dev See {IERC4626-deposit}. */
function deposit(uint256 assets, address receiver) public virtual override returns (uint256) {
require(assets <= maxDeposit(receiver), "ERC4626: deposit more than max");
uint256 shares = previewDeposit(assets);
_deposit(_msgSender(), receiver, assets, shares);
return shares;
}
/** @dev See {IERC4626-mint}.
*
* As opposed to {deposit}, minting is allowed even if the vault is in a state where the price of a share is zero.
* In this case, the shares will be minted without requiring any assets to be deposited.
*/
function mint(uint256 shares, address receiver) public virtual override returns (uint256) {
require(shares <= maxMint(receiver), "ERC4626: mint more than max");
uint256 assets = previewMint(shares);
_deposit(_msgSender(), receiver, assets, shares);
return assets;
}
/** @dev See {IERC4626-withdraw}. */
function withdraw(
uint256 assets,
address receiver,
address owner
) public virtual override returns (uint256) {
require(assets <= maxWithdraw(owner), "ERC4626: withdraw more than max");
uint256 shares = previewWithdraw(assets);
_withdraw(_msgSender(), receiver, owner, assets, shares);
return shares;
}
/** @dev See {IERC4626-redeem}. */
function redeem(
uint256 shares,
address receiver,
address owner
) public virtual override returns (uint256) {
require(shares <= maxRedeem(owner), "ERC4626: redeem more than max");
uint256 assets = previewRedeem(shares);
_withdraw(_msgSender(), receiver, owner, assets, shares);
return assets;
}
/**
* @dev Internal conversion function (from assets to shares) with support for rounding direction.
*
* Will revert if assets > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset
* would represent an infinite amount of shares.
*/
function _convertToShares(uint256 assets, MathUpgradeable.Rounding rounding) internal view virtual returns (uint256 shares) {
uint256 supply = totalSupply();
return
(assets == 0 || supply == 0)
? _initialConvertToShares(assets, rounding)
: assets.mulDiv(supply, totalAssets(), rounding);
}
/**
* @dev Internal conversion function (from assets to shares) to apply when the vault is empty.
*
* NOTE: Make sure to keep this function consistent with {_initialConvertToAssets} when overriding it.
*/
function _initialConvertToShares(
uint256 assets,
MathUpgradeable.Rounding /*rounding*/
) internal view virtual returns (uint256 shares) {
return assets;
}
/**
* @dev Internal conversion function (from shares to assets) with support for rounding direction.
*/
function _convertToAssets(uint256 shares, MathUpgradeable.Rounding rounding) internal view virtual returns (uint256 assets) {
uint256 supply = totalSupply();
return
(supply == 0) ? _initialConvertToAssets(shares, rounding) : shares.mulDiv(totalAssets(), supply, rounding);
}
/**
* @dev Internal conversion function (from shares to assets) to apply when the vault is empty.
*
* NOTE: Make sure to keep this function consistent with {_initialConvertToShares} when overriding it.
*/
function _initialConvertToAssets(
uint256 shares,
MathUpgradeable.Rounding /*rounding*/
) internal view virtual returns (uint256 assets) {
return shares;
}
/**
* @dev Deposit/mint common workflow.
*/
function _deposit(
address caller,
address receiver,
uint256 assets,
uint256 shares
) internal virtual {
// If _asset is ERC777, `transferFrom` can trigger a reenterancy BEFORE the transfer happens through the
// `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer,
// calls the vault, which is assumed not malicious.
//
// Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the
// assets are transferred and before the shares are minted, which is a valid state.
// slither-disable-next-line reentrancy-no-eth
SafeERC20Upgradeable.safeTransferFrom(_asset, caller, address(this), assets);
_mint(receiver, shares);
emit Deposit(caller, receiver, assets, shares);
}
/**
* @dev Withdraw/redeem common workflow.
*/
function _withdraw(
address caller,
address receiver,
address owner,
uint256 assets,
uint256 shares
) internal virtual {
if (caller != owner) {
_spendAllowance(owner, caller, shares);
}
// If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the
// `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer,
// calls the vault, which is assumed not malicious.
//
// Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the
// shares are burned and after the assets are transferred, which is a valid state.
_burn(owner, shares);
SafeERC20Upgradeable.safeTransfer(_asset, receiver, assets);
emit Withdraw(caller, receiver, owner, assets, shares);
}
/**
* @dev Checks if vault is "healthy" in the sense of having assets backing the circulating shares.
*/
function _isVaultCollateralized() private view returns (bool) {
return totalAssets() > 0 || totalSupply() == 0;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20Upgradeable.sol";
import "../extensions/draft-IERC20PermitUpgradeable.sol";
import "../../../utils/AddressUpgradeable.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 SafeERC20Upgradeable {
using AddressUpgradeable for address;
function safeTransfer(
IERC20Upgradeable token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20Upgradeable 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(
IERC20Upgradeable 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'
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(
IERC20Upgradeable token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20Upgradeable token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function safePermit(
IERC20PermitUpgradeable token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @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(IERC20Upgradeable 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
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC20Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol)
pragma solidity ^0.8.0;
import "../token/ERC20/IERC20Upgradeable.sol";
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/utils/cryptography/EIP712Upgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/cryptography/EIP712.sol)
pragma solidity ^0.8.0;
import "./ECDSAUpgradeable.sol";
import "../../proxy/utils/Initializable.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
* thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
* they need in their contracts using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* _Available since v3.4._
*
* @custom:storage-size 52
*/
abstract contract EIP712Upgradeable is Initializable {
/* solhint-disable var-name-mixedcase */
bytes32 private _HASHED_NAME;
bytes32 private _HASHED_VERSION;
bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
/* solhint-enable var-name-mixedcase */
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
function __EIP712_init(string memory name, string memory version) internal onlyInitializing {
__EIP712_init_unchained(name, version);
}
function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing {
bytes32 hashedName = keccak256(bytes(name));
bytes32 hashedVersion = keccak256(bytes(version));
_HASHED_NAME = hashedName;
_HASHED_VERSION = hashedVersion;
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash());
}
function _buildDomainSeparator(
bytes32 typeHash,
bytes32 nameHash,
bytes32 versionHash
) private view returns (bytes32) {
return keccak256(abi.encode(typeHash, nameHash, versionHash, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(), structHash);
}
/**
* @dev The hash of the name parameter for the EIP712 domain.
*
* NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
* are a concern.
*/
function _EIP712NameHash() internal virtual view returns (bytes32) {
return _HASHED_NAME;
}
/**
* @dev The hash of the version parameter for the EIP712 domain.
*
* NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs
* are a concern.
*/
function _EIP712VersionHash() internal virtual view returns (bytes32) {
return _HASHED_VERSION;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/utils/math/MathUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library MathUpgradeable {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @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.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// 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(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
return prod0 / denominator;
}
// 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].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
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 for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the 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.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // 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 preconditions 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 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10**64) {
value /= 10**64;
result += 64;
}
if (value >= 10**32) {
value /= 10**32;
result += 32;
}
if (value >= 10**16) {
value /= 10**16;
result += 16;
}
if (value >= 10**8) {
value /= 10**8;
result += 8;
}
if (value >= 10**4) {
value /= 10**4;
result += 4;
}
if (value >= 10**2) {
value /= 10**2;
result += 2;
}
if (value >= 10**1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
Submitted on: 2025-10-24 09:30:26
Comments
Log in to comment.
No comments yet.