Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

M-03 Fix [PAG] #102

Merged
merged 3 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 47 additions & 15 deletions src/vault/Vault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,22 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
int256 assets;
}

struct MarketsArgs {
IIonPool[] marketsToAdd;
uint256[] allocationCaps;
IIonPool[] newSupplyQueue;
IIonPool[] newWithdrawQueue;
}

constructor(
IERC20 _baseAsset,
address _feeRecipient,
uint256 _feePercentage,
string memory _name,
string memory _symbol,
uint48 initialDelay,
address initialDefaultAdmin
address initialDefaultAdmin,
MarketsArgs memory marketsArgs
)
ERC4626(_baseAsset)
ERC20(_name, _symbol)
Expand All @@ -105,6 +113,13 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
feeRecipient = _feeRecipient;

DECIMALS_OFFSET = uint8(_zeroFloorSub(uint256(18), IERC20Metadata(address(_baseAsset)).decimals()));

_addSupportedMarkets(
marketsArgs.marketsToAdd,
marketsArgs.allocationCaps,
marketsArgs.newSupplyQueue,
marketsArgs.newWithdrawQueue
);
}

/**
Expand Down Expand Up @@ -138,13 +153,24 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
* @param newWithdrawQueue Desired withdraw queue of IonPools for all resulting supported markets.
*/
function addSupportedMarkets(
IIonPool[] calldata marketsToAdd,
uint256[] calldata allocationCaps,
IIonPool[] calldata newSupplyQueue,
IIonPool[] calldata newWithdrawQueue
IIonPool[] memory marketsToAdd,
uint256[] memory allocationCaps,
IIonPool[] memory newSupplyQueue,
IIonPool[] memory newWithdrawQueue
)
public
external
onlyRole(OWNER_ROLE)
{
_addSupportedMarkets(marketsToAdd, allocationCaps, newSupplyQueue, newWithdrawQueue);
}

function _addSupportedMarkets(
IIonPool[] memory marketsToAdd,
uint256[] memory allocationCaps,
IIonPool[] memory newSupplyQueue,
IIonPool[] memory newWithdrawQueue
)
internal
{
if (marketsToAdd.length != allocationCaps.length) revert MarketsAndAllocationCapLengthMustBeEqual();

Expand All @@ -167,8 +193,8 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
}
}

updateSupplyQueue(newSupplyQueue);
updateWithdrawQueue(newWithdrawQueue);
_updateSupplyQueue(newSupplyQueue);
_updateWithdrawQueue(newWithdrawQueue);
}

/**
Expand Down Expand Up @@ -211,16 +237,20 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
++i;
}
}
updateSupplyQueue(newSupplyQueue);
updateWithdrawQueue(newWithdrawQueue);
_updateSupplyQueue(newSupplyQueue);
_updateWithdrawQueue(newWithdrawQueue);
}

/**
* @notice Update the order of the markets in which user deposits are supplied.
* @dev Each IonPool in the queue must be part of the `supportedMarkets` set.
* @param newSupplyQueue The new supply queue ordering.
*/
function updateSupplyQueue(IIonPool[] calldata newSupplyQueue) public onlyRole(ALLOCATOR_ROLE) {
function updateSupplyQueue(IIonPool[] memory newSupplyQueue) external onlyRole(ALLOCATOR_ROLE) {
_updateSupplyQueue(newSupplyQueue);
}

function _updateSupplyQueue(IIonPool[] memory newSupplyQueue) internal {
_validateQueueInput(newSupplyQueue);

supplyQueue = newSupplyQueue;
Expand All @@ -233,7 +263,11 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
* @dev The IonPool in the queue must be part of the `supportedMarkets` set.
* @param newWithdrawQueue The new withdraw queue ordering.
*/
function updateWithdrawQueue(IIonPool[] calldata newWithdrawQueue) public onlyRole(ALLOCATOR_ROLE) {
function updateWithdrawQueue(IIonPool[] memory newWithdrawQueue) external onlyRole(ALLOCATOR_ROLE) {
_updateWithdrawQueue(newWithdrawQueue);
}

function _updateWithdrawQueue(IIonPool[] memory newWithdrawQueue) internal {
_validateQueueInput(newWithdrawQueue);

withdrawQueue = newWithdrawQueue;
Expand All @@ -249,7 +283,7 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy
* The above rule enforces that the queue must have all and only the elements in the `supportedMarkets` set.
* @param queue The queue being validated.
*/
function _validateQueueInput(IIonPool[] calldata queue) internal view {
function _validateQueueInput(IIonPool[] memory queue) internal view {
uint256 _supportedMarketsLength = supportedMarkets.length();
uint256 queueLength = queue.length;

Expand Down Expand Up @@ -705,9 +739,7 @@ contract Vault is ERC4626, Multicall, AccessControlDefaultAdminRules, Reentrancy

function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal override {
super._deposit(caller, receiver, assets, shares);

_supplyToIonPool(assets);

_updateLastTotalAssets(lastTotalAssets + assets);
}

Expand Down
28 changes: 24 additions & 4 deletions src/vault/VaultFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ pragma solidity 0.8.21;

import { Vault } from "./Vault.sol";
import { IERC20 } from "openzeppelin-contracts/contracts/interfaces/IERC20.sol";
import { SafeERC20 } from "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";

/**
* @title Ion Lending Vault Factory
* @author Molecular Labs
* @notice Factory contract for deploying Ion Lending Vaults.
*/
contract VaultFactory {
using SafeERC20 for IERC20;

// --- Events ---

event CreateVault(
Expand All @@ -25,7 +28,13 @@ contract VaultFactory {
// --- External ---

/**
* @notice Deploys a new Ion Lending Vault.
* @notice Deploys a new Ion Lending Vault. Transfers the `initialDeposit`
* amount of the base asset from the caller initiate the first deposit to
* the vault. The minimum `initialDeposit` is 1e3. If less, this call would
* underflow as it will always burn 1e3 shares of the total shares minted to
* defend against inflation attacks.
* @dev The 1e3 initial deposit amount was chosen to defend against
* inflation attacks, referencing the UniV2 LP token implementation.
* @param baseAsset The asset that is being lent out to IonPools.
* @param feeRecipient Address that receives the accrued manager fees.
* @param feePercentage Fee percentage to be set.
Expand All @@ -34,6 +43,8 @@ contract VaultFactory {
* @param initialDelay The initial delay for default admin transfers.
* @param initialDefaultAdmin The initial default admin for the vault.
* @param salt The salt used for CREATE2 deployment.
* @param marketsArgs Arguments for the markets to be added to the vault.
* @param initialDeposit The initial deposit to be made to the vault.
*/
function createVault(
IERC20 baseAsset,
Expand All @@ -43,16 +54,25 @@ contract VaultFactory {
string memory symbol,
uint48 initialDelay,
address initialDefaultAdmin,
bytes32 salt
bytes32 salt,
Vault.MarketsArgs memory marketsArgs,
uint256 initialDeposit
)
external
returns (Vault vault)
{
// TODO use named args syntax
vault = new Vault{ salt: salt }(
baseAsset, feeRecipient, feePercentage, name, symbol, initialDelay, initialDefaultAdmin
baseAsset, feeRecipient, feePercentage, name, symbol, initialDelay, initialDefaultAdmin, marketsArgs
);

baseAsset.safeTransferFrom(msg.sender, address(this), initialDeposit);
baseAsset.approve(address(vault), initialDeposit);
uint256 sharesMinted = vault.deposit(initialDeposit, address(this));

// The factory keeps 1e3 shares to reduce inflation attack vector.
// Effectively burns this amount of shares by locking it in the factory.
vault.transfer(msg.sender, sharesMinted - 1e3);

emit CreateVault(address(vault), baseAsset, feeRecipient, feePercentage, name, symbol, initialDefaultAdmin);
}
}
Loading
Loading