Created
July 3, 2024 06:04
-
-
Save larrythecucumber321/36cb3dadf2ffef496ab12ef0149dffc2 to your computer and use it in GitHub Desktop.
Honey Contracts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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]; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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