Skip to content

Instantly share code, notes, and snippets.

@larrythecucumber321
Created July 3, 2024 06:04
Show Gist options
  • Save larrythecucumber321/36cb3dadf2ffef496ab12ef0149dffc2 to your computer and use it in GitHub Desktop.
Save larrythecucumber321/36cb3dadf2ffef496ab12ef0149dffc2 to your computer and use it in GitHub Desktop.
Honey Contracts
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import { ERC20 } from "solady/src/tokens/ERC20.sol";
import { ERC4626 } from "solady/src/tokens/ERC4626.sol";
import { FactoryOwnable } from "../base/FactoryOwnable.sol";
import { Utils } from "../libraries/Utils.sol";
import { IHoneyErrors } from "./IHoneyErrors.sol";
/// @notice This is the ERC4626 vault for the collateral assets to mint Honey.
/// @author Berachain Team
contract CollateralVault is ERC4626, PausableUpgradeable, FactoryOwnable, IHoneyErrors {
using Utils for bytes4;
ERC20 private _vaultAsset;
string private _name;
string private _symbol;
constructor() {
_disableInitializers();
}
function initialize(address asset_) external initializer {
__FactoryOwnable_init(msg.sender);
ERC20 _asset = ERC20(asset_);
_vaultAsset = _asset;
_name = string.concat(_asset.name(), "Vault");
_symbol = string.concat(_asset.symbol(), "Vault");
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Throws if called by any account other than the factory.
modifier onlyFactory() {
_checkFactory();
_;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ADMIN FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function pause() external onlyFactoryOwner {
_pause();
}
function unpause() external onlyFactoryOwner {
_unpause();
}
/**
* @dev A wrapper to ERC4626.deposit that only VaultAdmin can call.
* @dev It is a protection against inflation attacks,
* @dev in which only VaultAdmin can mint/burn shares.
* @param assets The assets to deposit into the vault to receive shares.
* @param receiver The address that will receive the shares.
* @return shares The shares minted to deposit the assets.
*/
function deposit(uint256 assets, address receiver) public override onlyFactory whenNotPaused returns (uint256) {
return super.deposit(assets, receiver);
}
/**
* @dev A wrapper to ERC4626.mint that only VaultAdmin can call.
* @dev It is a protection against inflation attacks,
* @dev in which only VaultAdmin can mint/burn shares.
* @param shares The exact shares to mint by depositing the assets.
* @param receiver The address that will receive the shares.
* @return assets The assets required to mint the shares.
*/
function mint(uint256 shares, address receiver) public override onlyFactory whenNotPaused returns (uint256) {
return super.mint(shares, receiver);
}
/**
* @dev A wrapper to ERC4626.withdraw that only VaultAdmin can call.
* @dev It is a protection against inflation attacks,
* @dev in which only VaultAdmin can mint/burn shares.
* @param assets The exact assets to withdraw from the vault.
* @param receiver The address that will receive the assets.
* @param owner The address that will burn the shares.
* @return shares The shares burned to withdraw the assets.
*/
function withdraw(
uint256 assets,
address receiver,
address owner
)
public
override
onlyFactory
whenNotPaused
returns (uint256)
{
return super.withdraw(assets, receiver, owner);
}
/**
* @dev A wrapper to ERC4626.redeem that only VaultAdmin can call.
* @dev It is a protection against inflation attacks,
* @dev in which only VaultAdmin can mint/burn shares.
* @param shares The shares to redeem for the assets.
* @param receiver The address that will receive the assets.
* @param owner The address that will burn the shares.
* @return assets The assets redeemed from the vault.
*/
function redeem(
uint256 shares,
address receiver,
address owner
)
public
override
onlyFactory
whenNotPaused
returns (uint256)
{
return super.redeem(shares, receiver, owner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* GETTERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function name() public view override returns (string memory) {
return _name;
}
function symbol() public view override returns (string memory) {
return _symbol;
}
function asset() public view virtual override returns (address) {
return address(_vaultAsset);
}
/**
* @dev An implementation of ERC4626.totalAssets to avoid inflation attacks,
* @dev which returns the total assets that VaultAdmin transferred to the vault.
*/
function totalAssets() public view override returns (uint256) {
// This is another layer of protection against inflation attacks.
// ERC4626 uses this function to calculate the exchange rate.
// Because only VaultAdmin can mint/burn shares via deposit/mint,
// the total assets transferred by VaultAdmin into this vault
// must be always equal to the total supply of the shares.
// Therefore, the assets/shares exchange rate is always 1.
// Attackers or users can transfer assets directly into this vault
// (thus the total assets is always greater than or equal the total supply),
// but that will not change the total assets returned by this
// function, thus the exchange rate is not changed.
//
// We also need to consider the difference in decimals
// between the vault and the asset to ensure that
// 10^assetDecimals asset_wei ~ 10^vaultDecimals vault_wei.
// asset_wei_totalAssets ~ vault_wei_totalSupply / 10**(vaultDecimals - assetDecimals)
return _convertToAssets(totalSupply());
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @inheritdoc ERC4626
function _initialConvertToShares(uint256 assets) internal view override returns (uint256 shares) {
return _convertToShares(assets);
}
/// @inheritdoc ERC4626
function _initialConvertToAssets(uint256 shares) internal view override returns (uint256 assets) {
return _convertToAssets(shares);
}
/**
* @dev Convert the assets to shares with decimals in consideration,
* but out of the ERC4626's logic to enforce the fixed exchange rate
* between Honey, shares, and assets.
* @param assets The assets to convert to shares.
* @return shares The shares converted from the assets.
*/
function _convertToShares(uint256 assets) private view returns (uint256 shares) {
uint8 vaultDecimals = decimals();
uint8 assetDecimals = _vaultAsset.decimals();
uint256 exponent;
if (vaultDecimals >= assetDecimals) {
unchecked {
exponent = vaultDecimals - assetDecimals;
}
return assets * (10 ** exponent);
}
unchecked {
exponent = assetDecimals - vaultDecimals;
}
return assets / (10 ** exponent);
}
/**
* @dev Convert the assets to shares with decimals in consideration,
* but out of the ERC4626's logic to enforce the fixed exchange rate
* between Honey, shares, and assets.
* @param shares The shares to convert to assets.
* @return assets The assets converted from the shares.
*/
function _convertToAssets(uint256 shares) private view returns (uint256 assets) {
uint8 vaultDecimals = decimals();
uint8 assetDecimals = _vaultAsset.decimals();
uint256 exponent;
if (vaultDecimals >= assetDecimals) {
unchecked {
exponent = vaultDecimals - assetDecimals;
}
return shares / (10 ** exponent);
}
unchecked {
exponent = assetDecimals - vaultDecimals;
}
return shares * (10 ** exponent);
}
/// @inheritdoc ERC4626
function _useVirtualShares() internal pure override returns (bool) {
return false;
}
function _checkFactory() internal view {
if (msg.sender != factory()) {
NotFactory.selector.revertWith();
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import { ERC20 } from "solady/src/tokens/ERC20.sol";
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { Utils } from "../libraries/Utils.sol";
import { IHoneyErrors } from "./IHoneyErrors.sol";
/// @notice This is the ERC20 token representation of Berachain's native stablecoin, Honey.
/// @author Berachain Team
contract Honey is ERC20, OwnableUpgradeable, UUPSUpgradeable, IHoneyErrors {
using Utils for bytes4;
string private constant NAME = "Honey";
string private constant SYMBOL = "HONEY";
/// @notice The factory contract that mints and burns Honey.
address public factory;
constructor() {
_disableInitializers();
}
function initialize(address _governance, address _factory) external initializer {
__Ownable_init(_governance);
factory = _factory;
}
function _authorizeUpgrade(address) internal override onlyOwner { }
modifier onlyFactory() {
if (msg.sender != factory) NotFactory.selector.revertWith();
_;
}
/// @notice Mint Honey to the receiver.
/// @dev Only the factory can call this function.
/// @param to The receiver address.
/// @param amount The amount of Honey to mint.
function mint(address to, uint256 amount) external onlyFactory {
_mint(to, amount);
}
/// @notice Burn Honey from an account.
/// @dev Only the factory can call this function.
/// @param from The account to burn Honey from.
/// @param amount The amount of Honey to burn.
function burn(address from, uint256 amount) external onlyFactory {
_burn(from, amount);
}
function name() public pure override returns (string memory) {
return NAME;
}
function symbol() public pure override returns (string memory) {
return SYMBOL;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import { UD60x18, ud } from "@prb/math/src/UD60x18.sol";
import { ERC20 } from "solady/src/tokens/ERC20.sol";
import { ERC4626 } from "solady/src/tokens/ERC4626.sol";
import { SafeTransferLib } from "solady/src/utils/SafeTransferLib.sol";
import { Utils } from "../libraries/Utils.sol";
import { IHoneyFactory } from "./IHoneyFactory.sol";
import { Honey } from "./Honey.sol";
import { VaultAdmin } from "./VaultAdmin.sol";
/// @notice This is the factory contract for minting and redeeming Honey.
/// @author Berachain Team
contract HoneyFactory is IHoneyFactory, VaultAdmin {
using Utils for bytes4;
/// @dev The constant representing 100% of mint/redeem rate.
uint256 private constant ONE_HUNDRED_PERCENT_RATE = 1e18;
/// @notice The Honey token contract.
Honey public honey;
/// @notice Mint rate of Honey for each asset, 60.18-decimal fixed-point number representation
mapping(address asset => uint256 rate) internal mintRates;
/// @notice Redemption rate of Honey for each asset, 60.18-decimal fixed-point number representation
mapping(address asset => uint256 rate) internal redeemRates;
constructor() {
_disableInitializers();
}
function initialize(address _governance, address _honey) external initializer {
__VaultAdmin_init(_governance);
honey = Honey(_honey);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ADMIN FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice Set the mint rate of Honey for an asset.
function setMintRate(address asset, uint256 mintRate) external onlyOwner {
if (mintRate > ONE_HUNDRED_PERCENT_RATE) {
OverOneHundredPercentRate.selector.revertWith(mintRate);
}
mintRates[asset] = mintRate;
emit MintRateSet(asset, mintRate);
}
/// @notice Set the redemption rate of Honey for an asset.
function setRedeemRate(address asset, uint256 redeemRate) external onlyOwner {
if (redeemRate > ONE_HUNDRED_PERCENT_RATE) {
OverOneHundredPercentRate.selector.revertWith(redeemRate);
}
redeemRates[asset] = redeemRate;
emit RedeemRateSet(asset, redeemRate);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* MODIFIERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Check the invariant of the vault to ensure that assets are always sufficient to redeem.
modifier checkInvariants(address asset) {
_;
ERC4626 vault = vaults[asset];
uint256 totalShares = vault.totalSupply();
uint256 totalAssets = ERC20(asset).balanceOf(address(vault));
if (vault.convertToAssets(totalShares) > totalAssets) {
InsufficientAssets.selector.revertWith(totalAssets, totalShares);
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* USER FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @inheritdoc IHoneyFactory
function mint(
address asset,
uint256 amount,
address receiver
)
external
onlyRegisteredAsset(asset)
whenNotPaused
checkInvariants(asset)
returns (uint256)
{
ERC4626 vault = vaults[asset];
// The sender transfers the assets into the factory.
// The sender needs to approve the assets to the factory before calling this function.
SafeTransferLib.safeTransferFrom(asset, msg.sender, address(this), amount);
// The factory approves the Vault for the assets.
SafeTransferLib.safeApprove(asset, address(vault), amount);
// The factory deposits the assets into Vault to mint the corresponding amount of shares.
uint256 shares = vault.deposit(amount, address(this));
// The factory mints the corresponding amount of Honey to the receiver
// with the consideration of the static mint fee.
(uint256 honeyToMint, uint256 feeShares) = _getHoneyMintedFromShares(asset, shares);
// The mint fee is transferred to the fee receiver.
vault.transfer(feeReceiver, feeShares);
honey.mint(receiver, honeyToMint);
emit HoneyMinted(msg.sender, receiver, asset, amount, honeyToMint);
return honeyToMint;
}
/// @inheritdoc IHoneyFactory
function redeem(
address asset,
uint256 honeyAmount,
address receiver
)
external
onlyRegisteredAsset(asset)
whenNotPaused
checkInvariants(asset)
returns (uint256)
{
ERC4626 vault = vaults[asset];
// The function reverts if the sender does not have enough Honey to burn or
// the vault does not have enough assets to redeem.
// The factory burns the corresponding amount of Honey of the sender
// to get the shares and redeem them for assets from the vault.
uint256 redeemedAssets;
{
(uint256 sharesForRedeem, uint256 feeShares) = _getSharesRedeemedFromHoney(asset, honeyAmount);
honey.burn(msg.sender, honeyAmount);
// The redeem fee is transferred to the fee receiver.
vault.transfer(feeReceiver, feeShares);
// The factory redeems the corresponding amount of assets from Vault
// and transfer the assets to the receiver.
redeemedAssets = vault.redeem(sharesForRedeem, receiver, address(this));
}
emit HoneyRedeemed(msg.sender, receiver, asset, redeemedAssets, honeyAmount);
return redeemedAssets;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* GETTERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @inheritdoc IHoneyFactory
function previewMint(address asset, uint256 amount) external view returns (uint256 honeyAmount) {
ERC4626 vault = vaults[asset];
// Get shares for a given assets.
uint256 shares = vault.previewDeposit(amount);
(honeyAmount,) = _getHoneyMintedFromShares(asset, shares);
}
/// @inheritdoc IHoneyFactory
function previewRedeem(address asset, uint256 honeyAmount) external view returns (uint256) {
ERC4626 vault = vaults[asset];
(uint256 shares,) = _getSharesRedeemedFromHoney(asset, honeyAmount);
// Get assets for a given shares.
return vault.previewRedeem(shares);
}
/// @inheritdoc IHoneyFactory
function previewRequiredCollateral(address asset, uint256 exactHoneyAmount) external view returns (uint256) {
ERC4626 vault = vaults[asset];
uint256 mintRate = _getMintRate(asset);
UD60x18 shares = ud(exactHoneyAmount).div(ud(mintRate));
// Get assets for an exact shares.
return vault.previewMint(UD60x18.unwrap(shares));
}
/// @inheritdoc IHoneyFactory
function previewHoneyToRedeem(address asset, uint256 exactAmount) external view returns (uint256) {
ERC4626 vault = vaults[asset];
// Get shares for an exact assets.
uint256 shares = vault.previewWithdraw(exactAmount);
uint256 redeemRate = _getRedeemRate(asset);
UD60x18 honeyAmount = ud(shares).div(ud(redeemRate));
return UD60x18.unwrap(honeyAmount);
}
/// @inheritdoc IHoneyFactory
function getMintRate(address asset) external view returns (uint256) {
return _getMintRate(asset);
}
/// @inheritdoc IHoneyFactory
function getRedeemRate(address asset) external view returns (uint256) {
return _getRedeemRate(asset);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INTERNAL FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function _getHoneyMintedFromShares(
address asset,
uint256 shares
)
internal
view
returns (uint256 honeyAmount, uint256 feeShares)
{
uint256 mintRate = _getMintRate(asset);
honeyAmount = ud(shares).mul(ud(mintRate)).unwrap();
feeShares = shares - honeyAmount;
}
function _getSharesRedeemedFromHoney(
address asset,
uint256 honeyAmount
)
internal
view
returns (uint256 shares, uint256 feeShares)
{
uint256 redeemRate = _getRedeemRate(asset);
shares = ud(honeyAmount).mul(ud(redeemRate)).unwrap();
feeShares = honeyAmount - shares;
}
function _getMintRate(address asset) internal view returns (uint256) {
return mintRates[asset];
}
function _getRedeemRate(address asset) internal view returns (uint256) {
return redeemRates[asset];
}
}
// SPDX-License-Identifier: MIT
// To support named parameters in mapping types and custom operators for user-defined value types.
pragma solidity ^0.8.19;
import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import { UUPSUpgradeable } from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import { ERC20 } from "solady/src/tokens/ERC20.sol";
import { ERC4626 } from "solady/src/tokens/ERC4626.sol";
import { LibClone } from "solady/src/utils/LibClone.sol";
import { UpgradeableBeacon } from "solady/src/utils/UpgradeableBeacon.sol";
import { Utils } from "../libraries/Utils.sol";
import { IHoneyErrors } from "./IHoneyErrors.sol";
import { CollateralVault } from "./CollateralVault.sol";
/// @notice This is the admin contract that manages the vaults and fees.
/// @author Berachain Team
abstract contract VaultAdmin is OwnableUpgradeable, PausableUpgradeable, UUPSUpgradeable, IHoneyErrors {
using Utils for bytes4;
/// @notice Emitted when the fee receiver address is set.
event FeeReceiverSet(address indexed feeReceiver);
/// @notice Emitted when a new vault is created.
event VaultCreated(address indexed vault, address indexed asset);
/// @notice The beacon address.
address public beacon;
/// @notice The address of the fee receiver.
address public feeReceiver;
/// @notice Array of registered assets.
address[] public registeredAssets;
/// @notice Mapping of assets to their corresponding vaults.
mapping(address asset => ERC4626 vault) public vaults;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* INITIALIZER */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Must be called by the initializer of the inheriting contract.
/// @param _governance The address of the governance.
function __VaultAdmin_init(address _governance) internal onlyInitializing {
__Ownable_init(_governance);
beacon = address(new UpgradeableBeacon(_governance, address(new CollateralVault())));
feeReceiver = _governance;
}
modifier onlyRegisteredAsset(address asset) {
if (address(vaults[asset]) == address(0)) {
AssetNotRegistered.selector.revertWith(address(asset));
}
_;
}
modifier onlyFeeReceiver() {
if (msg.sender != feeReceiver) {
UnauthorizedCaller.selector.revertWith(msg.sender, feeReceiver);
}
_;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ADMIN FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
function _authorizeUpgrade(address newImplementation) internal virtual override onlyOwner { }
/// @notice Pause the contract.
function pause() external onlyOwner {
_pause();
}
/// @notice Unpause the contract.
function unpause() external onlyOwner {
_unpause();
}
/// @dev Create a new ERC4626 vault for a pair of asset - Honey and register it with VaultAdmin.
/// @param asset The asset to create a vault for.
/// @return The newly created vault.
function createVault(address asset) external onlyOwner returns (ERC4626) {
if (address(vaults[asset]) != address(0)) {
VaultAlreadyRegistered.selector.revertWith(address(asset));
}
registeredAssets.push(asset);
// Use solady library to deploy deterministic beacon proxy.
bytes32 salt;
assembly ("memory-safe") {
mstore(0, asset)
salt := keccak256(0, 0x20)
}
CollateralVault vault = CollateralVault(LibClone.deployDeterministicERC1967BeaconProxy(beacon, salt));
vault.initialize(asset);
vaults[asset] = vault;
emit VaultCreated(address(vault), address(asset));
return vault;
}
/// @notice Set the fee receiver address.
function setFeeReceiver(address _feeReceiver) external onlyOwner {
require(_feeReceiver != address(0));
feeReceiver = _feeReceiver;
emit FeeReceiverSet(_feeReceiver);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* FEE RELATED */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice Fee receiver redeems all shares for assets.
function withdrawAllFee(address receiver) external onlyFeeReceiver {
uint256 numAssets = numRegisteredAssets();
for (uint256 i; i < numAssets;) {
ERC4626 vault = vaults[registeredAssets[i]];
vault.redeem(vault.balanceOf(feeReceiver), receiver, feeReceiver);
unchecked {
++i;
}
}
}
/// @notice Fee receiver redeems shares for an asset.
function withdrawFee(
address asset,
uint256 shares,
address receiver
)
external
onlyFeeReceiver
onlyRegisteredAsset(asset)
returns (uint256 assets)
{
assets = vaults[asset].redeem(shares, receiver, feeReceiver);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* GETTERS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @notice Get the length of `registeredAssets` array.
function numRegisteredAssets() public view returns (uint256) {
return registeredAssets.length;
}
/// @notice Predicts the address of the vault for the given asset.
/// @param asset The address of the asset.
/// @return The address of the vault.
function predictVaultAddress(address asset) external view returns (address) {
bytes32 salt;
assembly ("memory-safe") {
mstore(0, asset)
salt := keccak256(0, 0x20)
}
return LibClone.predictDeterministicAddressERC1967BeaconProxy(beacon, salt, address(this));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment